@flyo/nitro-astro 2.2.0 → 2.3.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/README.md CHANGED
@@ -22,14 +22,21 @@ npm install @flyo/nitro-astro
22
22
  Then, revise and adjust the configuration in your `astro.config.mjs`:
23
23
 
24
24
  ```js
25
+ import { loadEnv } from "vite"
26
+ import { defineConfig } from "astro/config";
25
27
  import flyoNitroIntegration from "@flyo/nitro-astro";
26
28
 
29
+ const {
30
+ FLYO_ACCESS_TOKEN,
31
+ FLYO_LIVE_EDIT
32
+ } = loadEnv(process.env.NODE_ENV, process.cwd() + "/", "");
33
+
27
34
  export default defineConfig({
28
35
  site: "https://myflyowebsite.com", // required to make the sitemap.xml work
29
36
  integrations: [
30
37
  flyoNitroIntegration({
31
- accessToken: "ADD_YOUR_TOKEN_HERE", // Switch between dev and prod tokens depending on the environment
32
- liveEdit: true, // Enable on dev and preview systems for application reloading in the Flyo preview frame upon changes
38
+ accessToken: FLYO_ACCESS_TOKEN, // Switch between dev and prod tokens depending on the environment
39
+ liveEdit: FLYO_LIVE_EDIT, // Enable on dev and preview systems for application reloading in the Flyo preview frame upon changes
33
40
  components: {
34
41
  // Define where the Flyo components are located. The suffix .astro is not required. The object key is the value from Flyo, while the object value is the component in the Astro components folder
35
42
  // [!] Adding new elements requires restarting the development process
@@ -45,6 +52,59 @@ export default defineConfig({
45
52
  > [!WARNING]
46
53
  > The nitro astro integration requires an SSR setup which is done by using `output: 'server'`.
47
54
 
55
+ ## Configuration Options
56
+
57
+ The `flyoNitroIntegration` accepts the following configuration options:
58
+
59
+ ### Required Options
60
+
61
+ - **`accessToken`** (string, required): Your Flyo access token for authentication. This is either the production or development token from the Flyo Cloud interface. Keep in mind that requests with production accessToken will be effectively cached by the Flyo CDN, but development accessToken requests will not be cached.
62
+
63
+ - **`components`** (object, required): Object containing component definitions where the key is the component name defined in the Flyo Interface, and the value is the name of the component inside the components directory. The `.astro` suffix is not required.
64
+ ```js
65
+ components: {
66
+ Text: "Text",
67
+ CardsGrid: "CardsGrid",
68
+ SlotContainer: "subfolder/SlotContainer",
69
+ }
70
+ ```
71
+
72
+ ### Optional Options
73
+
74
+ - **`liveEdit`** (string | boolean | number, default: `false`): Enables live editing mode. If enabled, the application will reload when changes are made in the Flyo preview frame. This should be enabled on dev and preview systems.
75
+
76
+ - **`componentsDir`** (string, default: `"src/components/flyo"`): Directory path where your Flyo components are located.
77
+
78
+ - **`fallbackComponent`** (string, optional): Name of a fallback component to use when a requested component is not found. This component will only be used in live editing mode. Example: `"BlockNotFound"` would reference `{componentsDir}/BlockNotFound.astro`.
79
+
80
+ - **`clientCacheHeaderTtl`** (number, default: `900`): TTL (Time-To-Live) for client-side cache headers, in seconds. Default is 900 seconds (15 minutes). Only available if `liveEdit` is disabled. Use `0` to disable client caching.
81
+
82
+ - **`serverCacheHeaderTtl`** (number, default: `1200`): TTL (Time-To-Live) for server-side cache headers, in seconds. Default is 1200 seconds (20 minutes). Only available if `liveEdit` is disabled. Use `0` to disable server caching.
83
+
84
+ ### Complete Configuration Example
85
+
86
+ ```js
87
+ export default defineConfig({
88
+ site: "https://myflyowebsite.com",
89
+ integrations: [
90
+ flyoNitroIntegration({
91
+ accessToken: FLYO_ACCESS_TOKEN,
92
+ liveEdit: FLYO_LIVE_EDIT,
93
+ componentsDir: "src/components/flyo",
94
+ fallbackComponent: "BlockNotFound",
95
+ clientCacheHeaderTtl: 600, // 10 minutes
96
+ serverCacheHeaderTtl: 1800, // 30 minutes
97
+ components: {
98
+ Text: "Text",
99
+ CardsGrid: "CardsGrid",
100
+ SlotContainer: "SlotContainer",
101
+ },
102
+ }),
103
+ ],
104
+ output: "server",
105
+ });
106
+ ```
107
+
48
108
  ### Pages
49
109
 
50
110
  Add a `[...slug].astro` file in the pages directory with the following example content as a catch-all CMS handler:
@@ -121,7 +181,26 @@ const currentPath = Astro.url.pathname;
121
181
 
122
182
  ### Blocks
123
183
 
124
- Block Component Example (which are mostly located in `src/components/flyo`):
184
+ Block components are the building blocks of your Flyo pages. They receive a `block` prop containing all the data from Flyo.
185
+
186
+ #### Basic Block Component Example
187
+
188
+ Located in `src/components/flyo/Text.astro`:
189
+
190
+ ```astro
191
+ ---
192
+ import { editable } from "@flyo/nitro-astro";
193
+ const { block } = Astro.props;
194
+ ---
195
+
196
+ <!-- Make the block editable if necessary -->
197
+ <div {...editable(block)}>
198
+ <!-- Content variable -->
199
+ <div set:html={block.content.content.html} />
200
+ </div>
201
+ ```
202
+
203
+ #### Block with Items and Images
125
204
 
126
205
  ```astro
127
206
  ---
@@ -159,14 +238,163 @@ const { block } = Astro.props;
159
238
  </div>
160
239
  ```
161
240
 
241
+ #### Cards Grid Component Example
242
+
243
+ Example of a component that displays a grid of cards with items (`src/components/flyo/CardsGrid.astro`):
244
+
245
+ ```astro
246
+ ---
247
+ import { Image } from "astro:assets";
248
+ import { editable } from "@flyo/nitro-astro";
249
+ const { block } = Astro.props;
250
+ ---
251
+
252
+ {
253
+ block.items.map((item: any) => (
254
+ <div {...editable(block)} style="background-color:#F0F0F0; padding:20px; margin-bottom:10px;">
255
+ <h2 class="text-xl">{item.title}</h2>
256
+ {item.image && (
257
+ <Image
258
+ src={item.image.source}
259
+ alt={item.title}
260
+ width="200"
261
+ height="200"
262
+ />
263
+ )}
264
+ <a href={item.link.routes.detail} class="underline">
265
+ Go to Detail
266
+ </a>
267
+ </div>
268
+ ))
269
+ }
270
+ ```
271
+
272
+ #### Slot Container Component Example
273
+
274
+ Slots allow you to nest blocks within blocks (`src/components/flyo/SlotContainer.astro`):
275
+
276
+ ```astro
277
+ ---
278
+ import BlockSlot from "@flyo/nitro-astro/BlockSlot.astro";
279
+ const { block } = Astro.props;
280
+ ---
281
+
282
+ <BlockSlot slot={block.slots.slotcontainername} />
283
+ ```
284
+
285
+ ## Available Components
286
+
287
+ ### Core Components
288
+
289
+ These components are provided by the package and can be imported directly:
290
+
291
+ #### `FlyoNitroPage`
292
+
293
+ Renders an entire Flyo page with all its blocks.
294
+
295
+ ```astro
296
+ ---
297
+ import FlyoNitroPage from "@flyo/nitro-astro/FlyoNitroPage.astro";
298
+ const page = await usePagesApi().page({ slug });
299
+ ---
300
+
301
+ <FlyoNitroPage page={page} />
302
+ ```
303
+
304
+ #### `FlyoNitroBlock`
305
+
306
+ Renders a single Flyo block. This component automatically maps the block's component name to your custom components.
307
+
308
+ ```astro
309
+ ---
310
+ import FlyoNitroBlock from "@flyo/nitro-astro/FlyoNitroBlock.astro";
311
+ ---
312
+
313
+ <FlyoNitroBlock block={block} />
314
+ ```
315
+
316
+ #### `BlockSlot`
317
+
318
+ Renders the contents of a Flyo block slot, allowing for nested block structures.
319
+
320
+ ```astro
321
+ ---
322
+ import BlockSlot from "@flyo/nitro-astro/BlockSlot.astro";
323
+ ---
324
+
325
+ <BlockSlot slot={block.slots.myslotname} />
326
+ ```
327
+
328
+ #### `MetaInfo`
329
+
330
+ Generic component for adding meta tags (title, description, image, JSON-LD).
331
+
332
+ ```astro
333
+ ---
334
+ import MetaInfo from "@flyo/nitro-astro/MetaInfo.astro";
335
+ ---
336
+
337
+ <MetaInfo
338
+ title="Page Title"
339
+ description="Page description"
340
+ image="https://example.com/image.jpg"
341
+ jsonld={jsonldObject}
342
+ slot="head"
343
+ />
344
+ ```
345
+
346
+ #### `MetaInfoPage`
347
+
348
+ Specialized meta component for Flyo pages.
349
+
350
+ ```astro
351
+ ---
352
+ import MetaInfoPage from "@flyo/nitro-astro/MetaInfoPage.astro";
353
+ ---
354
+
355
+ <MetaInfoPage page={page} slot="head" />
356
+ ```
357
+
358
+ #### `MetaInfoEntity`
359
+
360
+ Specialized meta component for Flyo entities.
361
+
362
+ ```astro
363
+ ---
364
+ import MetaInfoEntity from "@flyo/nitro-astro/MetaInfoEntity.astro";
365
+ ---
366
+
367
+ <MetaInfoEntity response={entityResponse} slot="head" />
368
+ ```
369
+
370
+ #### `FallbackComponent`
371
+
372
+ Automatically used when a component is not found (only in live edit mode). You can customize this behavior with the `fallbackComponent` configuration option.
373
+
162
374
  ### Wysiwyg
163
375
 
164
376
  The `FlyoWysiwyg` component renders ProseMirror/TipTap JSON content. It handles standard nodes automatically and allows you to provide custom components for specific node types.
165
377
 
378
+ #### Basic Usage
379
+
166
380
  ```astro
167
381
  ---
168
382
  import FlyoWysiwyg from "@flyo/nitro-astro/FlyoWysiwyg.astro";
169
- import CustomImage from "./CustomImage.astro";
383
+ const { block } = Astro.props;
384
+ ---
385
+
386
+ <FlyoWysiwyg json={block.content.json} />
387
+ ```
388
+
389
+ #### Custom Node Components
390
+
391
+ You can override the default rendering of specific node types by providing custom components:
392
+
393
+ ```astro
394
+ ---
395
+ import FlyoWysiwyg from "@flyo/nitro-astro/FlyoWysiwyg.astro";
396
+ import CustomImage from "./wysiwyg/CustomImage.astro";
397
+ import CustomVideo from "./wysiwyg/CustomVideo.astro";
170
398
 
171
399
  const { block } = Astro.props;
172
400
  ---
@@ -174,12 +402,15 @@ const { block } = Astro.props;
174
402
  <FlyoWysiwyg
175
403
  json={block.content.json}
176
404
  components={{
177
- image: CustomImage
405
+ image: CustomImage,
406
+ video: CustomVideo
178
407
  }}
179
408
  />
180
409
  ```
181
410
 
182
- And here is an example of how the `CustomImage.astro` component could look like:
411
+ #### Custom Image Component Example
412
+
413
+ Here's an example of a custom image component (`src/components/flyo/wysiwyg/Image.astro`):
183
414
 
184
415
  ```astro
185
416
  ---
@@ -187,7 +418,35 @@ const { node } = Astro.props;
187
418
  const { src, alt, title } = node.attrs;
188
419
  ---
189
420
 
190
- <img src={src} alt={alt} title={title} style="max-width: 100%; height: auto;" />
421
+ <img src={src.source} alt={alt} title={title} style="max-width: 100%; height: auto;" />
422
+ ```
423
+
424
+ The `node` prop contains all attributes from the ProseMirror node. For images, you typically get:
425
+ - `src`: The image source (can be an object with a `source` property when using Flyo storage)
426
+ - `alt`: Alternative text
427
+ - `title`: Image title
428
+
429
+ #### Complete Text Block with Wysiwyg Example
430
+
431
+ ```astro
432
+ ---
433
+ import { editable } from "@flyo/nitro-astro";
434
+ import FlyoWysiwyg from "@flyo/nitro-astro/FlyoWysiwyg.astro";
435
+ import Image from "./wysiwyg/Image.astro";
436
+
437
+ const { block } = Astro.props;
438
+ ---
439
+
440
+ <div {...editable(block)}>
441
+ <div class="p-4">
442
+ <FlyoWysiwyg
443
+ json={block.content.content.json}
444
+ components={{
445
+ image: Image
446
+ }}
447
+ />
448
+ </div>
449
+ </div>
191
450
  ```
192
451
 
193
452
  ### Entities
@@ -294,3 +553,270 @@ All endpoints accept a `lang` parameter to retrieve data in the desired language
294
553
  ```
295
554
 
296
555
  The above structure would be `/de/detail/[slug].astro` and `/fr/detail/[slug].astro`.
556
+
557
+ ## API Functions
558
+
559
+ The Flyo Nitro Astro package provides several API functions to interact with the Flyo Nitro CMS:
560
+
561
+ ### Configuration APIs
562
+
563
+ #### `useConfig(astro: AstroGlobal)`
564
+
565
+ Returns the resolved configuration object that includes navigation containers and other config data. This is typically used in layouts and pages.
566
+
567
+ ```astro
568
+ ---
569
+ import { useConfig } from "@flyo/nitro-astro";
570
+
571
+ const config = await useConfig(Astro);
572
+ // Access navigation items
573
+ config.containers.nav.items.map((item) => ...)
574
+ // Access available pages
575
+ config.pages.includes(slug)
576
+ ---
577
+ ```
578
+
579
+ #### `useConfigApi()`
580
+
581
+ Returns the ConfigApi instance for making custom configuration requests.
582
+
583
+ ```astro
584
+ ---
585
+ import { useConfigApi } from "@flyo/nitro-astro";
586
+
587
+ const configApi = useConfigApi();
588
+ const config = await configApi.config({ lang: "en" });
589
+ ---
590
+ ```
591
+
592
+ #### `useConfiguration()`
593
+
594
+ Returns the API main configuration which holds the access key and is globally available.
595
+
596
+ ```typescript
597
+ import { useConfiguration } from "@flyo/nitro-astro";
598
+
599
+ const configuration = useConfiguration();
600
+ ```
601
+
602
+ ### Page APIs
603
+
604
+ #### `usePagesApi()`
605
+
606
+ Returns the PagesApi instance for fetching Flyo pages.
607
+
608
+ ```astro
609
+ ---
610
+ import { usePagesApi } from "@flyo/nitro-astro";
611
+
612
+ const pagesApi = usePagesApi();
613
+ const page = await pagesApi.page({ slug: "about" });
614
+ ---
615
+ ```
616
+
617
+ ### Entity APIs
618
+
619
+ #### `useEntitiesApi()`
620
+
621
+ Returns the EntitiesApi instance for fetching entity details.
622
+
623
+ ```astro
624
+ ---
625
+ import { useEntitiesApi } from "@flyo/nitro-astro";
626
+
627
+ // Fetch by slug and type ID
628
+ const entity = await useEntitiesApi().entityBySlug({
629
+ slug: "my-post",
630
+ lang: Astro.currentLocale,
631
+ typeId: 54
632
+ });
633
+
634
+ // Or fetch by unique ID
635
+ const entity = await useEntitiesApi().entityByUniqueid({
636
+ uniqueid: "abc123",
637
+ lang: Astro.currentLocale
638
+ });
639
+ ---
640
+ ```
641
+
642
+ ### Search APIs
643
+
644
+ #### `useSearchApi()`
645
+
646
+ Returns the SearchApi instance for performing search operations.
647
+
648
+ ```astro
649
+ ---
650
+ import { useSearchApi } from "@flyo/nitro-astro";
651
+
652
+ const searchApi = useSearchApi();
653
+ // Use the search API for custom search functionality
654
+ ---
655
+ ```
656
+
657
+ ### Sitemap APIs
658
+
659
+ #### `useSitemapApi()`
660
+
661
+ Returns the SitemapApi instance. The sitemap is automatically generated at `/sitemap.xml`, but you can use this API for custom sitemap functionality.
662
+
663
+ ```astro
664
+ ---
665
+ import { useSitemapApi } from "@flyo/nitro-astro";
666
+
667
+ const sitemapApi = useSitemapApi();
668
+ const sitemap = await sitemapApi.sitemap();
669
+ ---
670
+ ```
671
+
672
+ ### Version APIs
673
+
674
+ #### `useVersionApi()`
675
+
676
+ Returns the VersionApi instance for checking API versions.
677
+
678
+ ```astro
679
+ ---
680
+ import { useVersionApi } from "@flyo/nitro-astro";
681
+
682
+ const versionApi = useVersionApi();
683
+ // Use for version-specific functionality
684
+ ---
685
+ ```
686
+
687
+ ### Helper Functions
688
+
689
+ #### `editable(block)`
690
+
691
+ Makes a block editable in the Flyo live edit mode. Returns an object with the `data-flyo-uid` attribute.
692
+
693
+ ```astro
694
+ ---
695
+ import { editable } from "@flyo/nitro-astro";
696
+ const { block } = Astro.props;
697
+ ---
698
+
699
+ <div {...editable(block)}>
700
+ <!-- Block content -->
701
+ </div>
702
+ ```
703
+
704
+ > **Note**: `editableBlock` is also available as a backwards-compatible alias for `editable`.
705
+
706
+ ## Built-in Features
707
+
708
+ ### Automatic Sitemap Generation
709
+
710
+ The integration automatically creates a `/sitemap.xml` route that includes all your Flyo pages and entities. Make sure to set the `site` property in your `astro.config.mjs`:
711
+
712
+ ```js
713
+ export default defineConfig({
714
+ site: "https://myflyowebsite.com",
715
+ // ... rest of config
716
+ });
717
+ ```
718
+
719
+ ### Image Service Integration
720
+
721
+ The package includes an automatic image service that integrates with Flyo Storage's image transformation capabilities. Images are automatically processed through Flyo's CDN with support for:
722
+
723
+ - Dynamic resizing (`width` and `height`)
724
+ - Format conversion (defaults to WebP)
725
+ - Lazy loading
726
+ - Proper dimensions to prevent CLS
727
+
728
+ ```astro
729
+ ---
730
+ import { Image } from "astro:assets";
731
+ ---
732
+
733
+ <Image
734
+ src={block.content.image.source}
735
+ alt="Description"
736
+ width={1920}
737
+ height={768}
738
+ />
739
+ ```
740
+
741
+ The image URL is automatically transformed to: `https://storage.flyo.cloud/image_xxx.jpg/thumb/1920x768?format=webp`
742
+
743
+ ### Development Toolbar
744
+
745
+ When running in development mode, the integration adds a custom toolbar with quick links to:
746
+ - Flyo Cloud Login
747
+ - Flyo Nitro Developer Portal
748
+ - Flyo Nitro API Documentation
749
+
750
+ ### Middleware & Caching
751
+
752
+ The integration automatically adds middleware that:
753
+ - Resolves the Flyo configuration on each request
754
+ - Sets appropriate cache headers for production (configurable via `clientCacheHeaderTtl` and `serverCacheHeaderTtl`)
755
+ - Disables caching when `liveEdit` is enabled
756
+
757
+ Cache headers set:
758
+ - `Cache-Control`: Client-side caching
759
+ - `CDN-Cache-Control`: CDN caching
760
+ - `Vercel-CDN-Cache-Control`: Vercel-specific caching
761
+
762
+ ### Live Edit Mode
763
+
764
+ When `liveEdit` is enabled, the integration:
765
+ - Injects JavaScript to enable page refresh from the Flyo interface
766
+ - Wires up all elements with `data-flyo-uid` for direct editing
767
+ - Highlights and enables click-to-edit functionality
768
+ - Shows fallback components when blocks are missing
769
+
770
+ ## Best Practices
771
+
772
+ ### Component Organization
773
+
774
+ Organize your Flyo components in a dedicated directory (default: `src/components/flyo`):
775
+
776
+ ```
777
+ src/
778
+ components/
779
+ flyo/
780
+ Text.astro
781
+ CardsGrid.astro
782
+ Hero.astro
783
+ wysiwyg/
784
+ Image.astro
785
+ Video.astro
786
+ ```
787
+
788
+ ### Error Handling
789
+
790
+ Always wrap API calls in try-catch blocks and return appropriate responses:
791
+
792
+ ```astro
793
+ ---
794
+ let page;
795
+ try {
796
+ page = await usePagesApi().page({ slug });
797
+ } catch (e) {
798
+ return new Response("Not Found", {
799
+ status: 404,
800
+ statusText: "Page Not Found"
801
+ });
802
+ }
803
+ ---
804
+ ```
805
+
806
+ ### Environment Variables
807
+
808
+ Use environment variables for sensitive configuration:
809
+
810
+ ```bash
811
+ # .env
812
+ FLYO_ACCESS_TOKEN=your_token_here
813
+ FLYO_LIVE_EDIT=true
814
+ ```
815
+
816
+ ### TypeScript Support
817
+
818
+ The package is fully typed and exports TypeScript types from `@flyo/nitro-typescript`:
819
+
820
+ ```typescript
821
+ import type { Block, Page, Entity } from "@flyo/nitro-typescript";
822
+ ```
@@ -0,0 +1,43 @@
1
+ ---
2
+ import { useFlyoIntegration, useConfig } from "@flyo/nitro-astro";
3
+
4
+ const integration = useFlyoIntegration();
5
+ const config = await useConfig(Astro);
6
+
7
+ // Get environment variables
8
+ const mode = import.meta.env.MODE;
9
+ const vercelDeploymentId = import.meta.env.VERCEL_DEPLOYMENT_ID || '-';
10
+ const vercelGitCommitSha = import.meta.env.VERCEL_GIT_COMMIT_SHA || '-';
11
+
12
+ // Get token and determine type
13
+ const token = integration.options.accessToken || '';
14
+ const tokenType = token.startsWith('p-') ? 'production' : (token.startsWith('d-') ? 'develop' : 'unknown');
15
+
16
+ // Get live edit / debug status
17
+ const debug = integration.options.liveEdit;
18
+
19
+ // Get API version from config.nitro
20
+ const apiVersion = config.nitro?.version?.toString() || '-';
21
+ const apiLastUpdate = config.nitro?.updated_at
22
+ ? new Date(config.nitro.updated_at * 1000).toLocaleString('de-CH', {
23
+ day: '2-digit',
24
+ month: '2-digit',
25
+ year: 'numeric',
26
+ hour: '2-digit',
27
+ minute: '2-digit'
28
+ })
29
+ : '-';
30
+
31
+ const debugInfo = [
32
+ `liveedit:${debug}`,
33
+ `mode:${mode}`,
34
+ `apiversion:${apiVersion}`,
35
+ `apiversiondate:${apiLastUpdate}`,
36
+ `tokentype:${tokenType}`,
37
+ `did:${vercelDeploymentId}`,
38
+ `csha:${vercelGitCommitSha}`
39
+ ].join(' | ');
40
+
41
+ ---
42
+
43
+ <Fragment set:html={`<!-- ${debugInfo} -->`} />
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import DebugInfo from './DebugInfo';
3
+
4
+ // Mock the dependencies
5
+ vi.mock('../index', () => ({
6
+ useFlyoIntegration: vi.fn(),
7
+ useConfig: vi.fn(),
8
+ }));
9
+
10
+ describe('DebugInfo', () => {
11
+ beforeEach(() => {
12
+ vi.clearAllMocks();
13
+
14
+ // Reset environment variables
15
+ vi.stubEnv('DEV', true);
16
+ vi.stubEnv('MODE', 'development');
17
+ vi.stubEnv('VERCEL_DEPLOYMENT_ID', '');
18
+ vi.stubEnv('VERCEL_GIT_COMMIT_SHA', '');
19
+ });
20
+
21
+ it('should export the component', () => {
22
+ expect(DebugInfo).toBeDefined();
23
+ });
24
+
25
+ it('should handle development token type', async () => {
26
+ const { useFlyoIntegration, useConfig } = await import('../index');
27
+
28
+ vi.mocked(useFlyoIntegration).mockReturnValue({
29
+ options: {
30
+ accessToken: 'd-development-token-123',
31
+ liveEdit: true,
32
+ componentsDir: 'src/components/flyo',
33
+ clientCacheHeaderTtl: 900,
34
+ serverCacheHeaderTtl: 1200,
35
+ },
36
+ } as any);
37
+
38
+ vi.mocked(useConfig).mockResolvedValue({
39
+ nitro: {
40
+ version: 42,
41
+ updated_at: 1706270400, // 2024-01-26 12:00:00 UTC
42
+ },
43
+ } as any);
44
+
45
+ expect(useFlyoIntegration).toBeDefined();
46
+ expect(useConfig).toBeDefined();
47
+ });
48
+
49
+ it('should handle production token type', async () => {
50
+ const { useFlyoIntegration } = await import('../index');
51
+
52
+ vi.mocked(useFlyoIntegration).mockReturnValue({
53
+ options: {
54
+ accessToken: 'p-production-token-456',
55
+ liveEdit: false,
56
+ componentsDir: 'src/components/flyo',
57
+ clientCacheHeaderTtl: 900,
58
+ serverCacheHeaderTtl: 1200,
59
+ },
60
+ } as any);
61
+
62
+ const integration = useFlyoIntegration();
63
+ const token = integration.options.accessToken as string;
64
+ const tokenType = token.startsWith('p-') ? 'production' : (token.startsWith('d-') ? 'develop' : 'unknown');
65
+
66
+ expect(tokenType).toBe('production');
67
+ });
68
+
69
+ it('should handle unknown token type', async () => {
70
+ const { useFlyoIntegration } = await import('../index');
71
+
72
+ vi.mocked(useFlyoIntegration).mockReturnValue({
73
+ options: {
74
+ accessToken: 'invalid-token',
75
+ liveEdit: false,
76
+ componentsDir: 'src/components/flyo',
77
+ clientCacheHeaderTtl: 900,
78
+ serverCacheHeaderTtl: 1200,
79
+ },
80
+ } as any);
81
+
82
+ const integration = useFlyoIntegration();
83
+ const token = integration.options.accessToken as string;
84
+ const tokenType = token.startsWith('p-') ? 'production' : (token.startsWith('d-') ? 'develop' : 'unknown');
85
+
86
+ expect(tokenType).toBe('unknown');
87
+ });
88
+
89
+ it('should handle missing nitro config', async () => {
90
+ const { useConfig } = await import('../index');
91
+
92
+ vi.mocked(useConfig).mockResolvedValue({} as any);
93
+
94
+ const config = await useConfig({} as any);
95
+ const apiVersion = config.nitro?.version?.toString() || '-';
96
+ const apiLastUpdate = config.nitro?.updated_at ? 'date' : '-';
97
+
98
+ expect(apiVersion).toBe('-');
99
+ expect(apiLastUpdate).toBe('-');
100
+ });
101
+
102
+ it('should format date correctly from unix timestamp', () => {
103
+ const timestamp = 1706270400; // 2024-01-26 12:00:00 UTC
104
+ const date = new Date(timestamp * 1000);
105
+
106
+ expect(date).toBeInstanceOf(Date);
107
+ expect(date.getTime()).toBe(1706270400000);
108
+ });
109
+
110
+ it('should handle vercel environment variables', () => {
111
+ vi.stubEnv('VERCEL_DEPLOYMENT_ID', 'dpl_abc123');
112
+ vi.stubEnv('VERCEL_GIT_COMMIT_SHA', 'a1b2c3d4e5f6');
113
+
114
+ const vercelDeploymentId = import.meta.env.VERCEL_DEPLOYMENT_ID || '-';
115
+ const vercelGitCommitSha = import.meta.env.VERCEL_GIT_COMMIT_SHA || '-';
116
+
117
+ expect(vercelDeploymentId).toBe('dpl_abc123');
118
+ expect(vercelGitCommitSha).toBe('a1b2c3d4e5f6');
119
+ });
120
+ });
@@ -0,0 +1,2 @@
1
+ import DebugInfo from "./DebugInfo.astro";
2
+ export default DebugInfo;
@@ -1,4 +1,4 @@
1
- (function(s,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):(s=typeof globalThis<"u"?globalThis:s||self,h(s.flyoNitroIntegration={}))})(this,function(s){"use strict";const h="https://api.flyo.cloud/nitro/v1".replace(/\/+$/,"");class ${constructor(n={}){this.configuration=n}set config(n){this.configuration=n}get basePath(){return this.configuration.basePath!=null?this.configuration.basePath:h}get fetchApi(){return this.configuration.fetchApi}get middleware(){return this.configuration.middleware||[]}get queryParamsStringify(){return this.configuration.queryParamsStringify||_}get username(){return this.configuration.username}get password(){return this.configuration.password}get apiKey(){const n=this.configuration.apiKey;if(n)return typeof n=="function"?n:()=>n}get accessToken(){const n=this.configuration.accessToken;if(n)return typeof n=="function"?n:async()=>n}get headers(){return this.configuration.headers}get credentials(){return this.configuration.credentials}}const x=new $,w=class F{constructor(n=x){this.configuration=n,this.fetchApi=async(t,i)=>{let r={url:t,init:i};for(const a of this.middleware)a.pre&&(r=await a.pre({fetch:this.fetchApi,...r})||r);let o;try{o=await(this.configuration.fetchApi||fetch)(r.url,r.init)}catch(a){for(const l of this.middleware)l.onError&&(o=await l.onError({fetch:this.fetchApi,url:r.url,init:r.init,error:a,response:o?o.clone():void 0})||o);if(o===void 0)throw a instanceof Error?new O(a,"The request failed and the interceptors did not return an alternative response"):a}for(const a of this.middleware)a.post&&(o=await a.post({fetch:this.fetchApi,url:r.url,init:r.init,response:o.clone()})||o);return o},this.middleware=n.middleware}withMiddleware(...n){const t=this.clone();return t.middleware=t.middleware.concat(...n),t}withPreMiddleware(...n){const t=n.map(i=>({pre:i}));return this.withMiddleware(...t)}withPostMiddleware(...n){const t=n.map(i=>({post:i}));return this.withMiddleware(...t)}isJsonMime(n){return n?F.jsonRegex.test(n):!1}async request(n,t){const{url:i,init:r}=await this.createFetchParams(n,t),o=await this.fetchApi(i,r);if(o&&o.status>=200&&o.status<300)return o;throw new N(o,"Response returned an error code")}async createFetchParams(n,t){let i=this.configuration.basePath+n.path;n.query!==void 0&&Object.keys(n.query).length!==0&&(i+="?"+this.configuration.queryParamsStringify(n.query));const r=Object.assign({},this.configuration.headers,n.headers);Object.keys(r).forEach(p=>r[p]===void 0?delete r[p]:{});const o=typeof t=="function"?t:async()=>t,a={method:n.method,headers:r,body:n.body,credentials:this.configuration.credentials},l={...a,...await o({init:a,context:n})};let u;M(l.body)||l.body instanceof URLSearchParams||K(l.body)?u=l.body:this.isJsonMime(r["Content-Type"])?u=JSON.stringify(l.body):u=l.body;const c={...l,body:u};return{url:i,init:c}}clone(){const n=this.constructor,t=new n(this.configuration);return t.middleware=this.middleware.slice(),t}};w.jsonRegex=new RegExp("^(:?application/json|[^;/ ]+/[^;/ ]+[+]json)[ ]*(:?;.*)?$","i");let g=w;function K(e){return typeof Blob<"u"&&e instanceof Blob}function M(e){return typeof FormData<"u"&&e instanceof FormData}class N extends Error{constructor(n,t){super(t),this.response=n,this.name="ResponseError"}}class O extends Error{constructor(n,t){super(t),this.cause=n,this.name="FetchError"}}class y extends Error{constructor(n,t){super(t),this.field=n,this.name="RequiredError"}}function _(e,n=""){return Object.keys(e).map(t=>b(t,e[t],n)).filter(t=>t.length>0).join("&")}function b(e,n,t=""){const i=t+(t.length?`[${e}]`:e);if(n instanceof Array){const r=n.map(o=>encodeURIComponent(String(o))).join(`&${encodeURIComponent(i)}=`);return`${encodeURIComponent(i)}=${r}`}if(n instanceof Set){const r=Array.from(n);return b(e,r,t)}return n instanceof Date?`${encodeURIComponent(i)}=${encodeURIComponent(n.toISOString())}`:n instanceof Object?_(n,i):`${encodeURIComponent(i)}=${encodeURIComponent(String(n))}`}function v(e,n){const t={};for(const i of Object.keys(e))t[i]=n(e[i]);return t}class d{constructor(n,t=i=>i){this.raw=n,this.transformer=t}async value(){return this.transformer(await this.raw.json())}}function D(e){return B(e)}function B(e,n){return e==null?e:{identifier:e.identifier==null?void 0:e.identifier,content:e.content==null?void 0:e.content.map(C)}}function C(e){return G(e)}function G(e,n){return e==null?e:{items:e.items==null?void 0:e.items,content:e.content==null?void 0:e.content,config:e.config==null?void 0:e.config,identifier:e.identifier==null?void 0:e.identifier,uid:e.uid==null?void 0:e.uid,component:e.component==null?void 0:e.component,slots:e.slots==null?void 0:v(e.slots,D)}}function E(e){return H(e)}function H(e,n){return e==null?e:{slug:e.slug==null?void 0:e.slug,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href}}function V(e){return z(e)}function z(e,n){return e==null?e:{domain:e.domain==null?void 0:e.domain,slug:e.slug==null?void 0:e.slug,version:e.version==null?void 0:e.version,updated_at:e.updated_at==null?void 0:e.updated_at,language:e.language==null?void 0:e.language,primary_language:e.primary_language==null?void 0:e.primary_language}}function q(e){return J(e)}function J(e,n){return e==null?e:{type:e.type==null?void 0:e.type,target:e.target==null?void 0:e.target,label:e.label==null?void 0:e.label,href:e.href==null?void 0:e.href,slug:e.slug==null?void 0:e.slug,properties:e.properties==null?void 0:e.properties,children:e.children==null?void 0:e.children.map(q)}}function W(e){return Q(e)}function Q(e,n){return e==null?e:{items:e.items==null?void 0:e.items.map(q),uid:e.uid==null?void 0:e.uid,identifier:e.identifier==null?void 0:e.identifier,label:e.label==null?void 0:e.label}}function X(e){return Y(e)}function Y(e,n){return e==null?e:{nitro:e.nitro==null?void 0:V(e.nitro),pages:e.pages==null?void 0:e.pages,containers:e.containers==null?void 0:v(e.containers,W),globals:e.globals==null?void 0:e.globals}}function Z(e){return ee(e)}function ee(e,n){return e==null?e:{shortcode:e.shortcode==null?void 0:e.shortcode,name:e.name==null?void 0:e.name}}function R(e){return ne(e)}function ne(e,n){return e==null?e:{language:e.language==null?void 0:Z(e.language),slug:e.slug==null?void 0:e.slug,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href}}function te(e){return ie(e)}function ie(e,n){return e==null?e:{api:e.api==null?void 0:e.api,image:e.image==null?void 0:e.image}}function oe(e){return re(e)}function re(e,n){return e==null?e:{_version:e._version==null?void 0:e._version,entity_metric:e.entity_metric==null?void 0:te(e.entity_metric),entity_unique_id:e.entity_unique_id==null?void 0:e.entity_unique_id,entity_id:e.entity_id==null?void 0:e.entity_id,entity_image:e.entity_image==null?void 0:e.entity_image,entity_slug:e.entity_slug==null?void 0:e.entity_slug,entity_teaser:e.entity_teaser==null?void 0:e.entity_teaser,entity_time_end:e.entity_time_end==null?void 0:e.entity_time_end,entity_time_start:e.entity_time_start==null?void 0:e.entity_time_start,entity_title:e.entity_title==null?void 0:e.entity_title,entity_type:e.entity_type==null?void 0:e.entity_type,entity_type_id:e.entity_type_id==null?void 0:e.entity_type_id,updated_at:e.updated_at==null?void 0:e.updated_at,routes:e.routes==null?void 0:e.routes}}function A(e){return ae(e)}function ae(e,n){return e==null?e:{entity:e.entity==null?void 0:oe(e.entity),model:e.model==null?void 0:e.model,language:e.language==null?void 0:e.language,jsonld:e.jsonld==null?void 0:e.jsonld,translation:e.translation==null?void 0:e.translation.map(R),breadcrumb:e.breadcrumb==null?void 0:e.breadcrumb.map(E)}}function I(e){return le(e)}function le(e,n){return e==null?e:{entity_unique_id:e.entity_unique_id==null?void 0:e.entity_unique_id,entity_title:e.entity_title==null?void 0:e.entity_title,entity_teaser:e.entity_teaser==null?void 0:e.entity_teaser,entity_slug:e.entity_slug==null?void 0:e.entity_slug,entity_time_start:e.entity_time_start==null?void 0:e.entity_time_start,entity_type:e.entity_type==null?void 0:e.entity_type,entity_type_id:e.entity_type_id==null?void 0:e.entity_type_id,entity_image:e.entity_image==null?void 0:e.entity_image,routes:e.routes==null?void 0:e.routes}}function se(e){return ue(e)}function ue(e,n){return e==null?e:{description:e.description==null?void 0:e.description,image:e.image==null?void 0:e.image,title:e.title==null?void 0:e.title}}function ce(e){return de(e)}function de(e,n){return e==null?e:{value:e.value==null?void 0:e.value,navigation:e.navigation==null?void 0:e.navigation,propagate:e.propagate==null?void 0:e.propagate}}function T(e){return fe(e)}function fe(e,n){return e==null?e:{id:e.id==null?void 0:e.id,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href,slug:e.slug==null?void 0:e.slug,json:e.json==null?void 0:e.json.map(C),depth:e.depth==null?void 0:e.depth,is_home:e.is_home==null?void 0:e.is_home,created_at:e.created_at==null?void 0:e.created_at,updated_at:e.updated_at==null?void 0:e.updated_at,is_visible:e.is_visible==null?void 0:e.is_visible,meta_json:e.meta_json==null?void 0:se(e.meta_json),properties:e.properties==null?void 0:v(e.properties,ce),uid:e.uid==null?void 0:e.uid,type:e.type==null?void 0:e.type,target:e.target==null?void 0:e.target,container:e.container==null?void 0:e.container,breadcrumb:e.breadcrumb==null?void 0:e.breadcrumb.map(E),translation:e.translation==null?void 0:e.translation.map(R)}}function ge(e){return he(e)}function he(e,n){return e==null?e:{version:e.version==null?void 0:e.version,updated_at:e.updated_at==null?void 0:e.updated_at}}class pe extends g{async configRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/config",method:"GET",headers:r,query:i},t);return new d(a,l=>X(l))}async config(n={},t){return await(await this.configRaw(n,t)).value()}}class ye extends g{async entityBySlugRaw(n,t){if(n.slug==null)throw new y("slug",'Required parameter "slug" was null or undefined when calling entityBySlug().');const i={};n.typeId!=null&&(i.typeId=n.typeId),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));let o="/entities/slug/{slug}";o=o.replace("{slug}",encodeURIComponent(String(n.slug)));const a=await this.request({path:o,method:"GET",headers:r,query:i},t);return new d(a,l=>A(l))}async entityBySlug(n,t){return await(await this.entityBySlugRaw(n,t)).value()}async entityByUniqueidRaw(n,t){if(n.uniqueid==null)throw new y("uniqueid",'Required parameter "uniqueid" was null or undefined when calling entityByUniqueid().');const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));let o="/entities/uniqueid/{uniqueid}";o=o.replace("{uniqueid}",encodeURIComponent(String(n.uniqueid)));const a=await this.request({path:o,method:"GET",headers:r,query:i},t);return new d(a,l=>A(l))}async entityByUniqueid(n,t){return await(await this.entityByUniqueidRaw(n,t)).value()}}class ve extends g{async homeRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/pages/home",method:"GET",headers:r,query:i},t);return new d(a,l=>T(l))}async home(n={},t){return await(await this.homeRaw(n,t)).value()}async pageRaw(n,t){const i={};n.slug!=null&&(i.slug=n.slug),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/pages",method:"GET",headers:r,query:i},t);return new d(a,l=>T(l))}async page(n={},t){return await(await this.pageRaw(n,t)).value()}}class me extends g{async searchRaw(n,t){if(n.query==null)throw new y("query",'Required parameter "query" was null or undefined when calling search().');const i={};n.query!=null&&(i.query=n.query),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/search",method:"GET",headers:r,query:i},t);return new d(a,l=>l.map(I))}async search(n,t){return await(await this.searchRaw(n,t)).value()}}class we extends g{async sitemapRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/sitemap",method:"GET",headers:r,query:i},t);return new d(a,l=>l.map(I))}async sitemap(n={},t){return await(await this.sitemapRaw(n,t)).value()}}class _e extends g{async versionRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/version",method:"GET",headers:r,query:i},t);return new d(a,l=>ge(l))}async version(n={},t){return await(await this.versionRaw(n,t)).value()}}const be=/[\p{Lu}]/u,Ce=/[\p{Ll}]/u,k=/^[\p{Lu}](?![\p{Lu}])/gu,S=/([\p{Alpha}\p{N}_]|$)/u,m=/[_.\- ]+/,Ee=new RegExp("^"+m.source),j=new RegExp(m.source+S.source,"gu"),L=new RegExp("\\d+"+S.source,"gu"),qe=(e,n,t,i)=>{let r=!1,o=!1,a=!1,l=!1;for(let u=0;u<e.length;u++){const c=e[u];l=u>2?e[u-3]==="-":!0,r&&be.test(c)?(e=e.slice(0,u)+"-"+e.slice(u),r=!1,a=o,o=!0,u++):o&&a&&Ce.test(c)&&(!l||i)?(e=e.slice(0,u-1)+"-"+e.slice(u-1),a=o,o=!1,r=!0):(r=n(c)===c&&t(c)!==c,a=o,o=t(c)===c&&n(c)!==c)}return e},Re=(e,n)=>(k.lastIndex=0,e.replaceAll(k,t=>n(t))),Ae=(e,n)=>(j.lastIndex=0,L.lastIndex=0,e.replaceAll(L,(t,i,r)=>["_","-"].includes(e.charAt(r+t.length))?t:n(t)).replaceAll(j,(t,i)=>n(i)));function Ie(e,n){if(!(typeof e=="string"||Array.isArray(e)))throw new TypeError("Expected the input to be `string | string[]`");if(n={pascalCase:!1,preserveConsecutiveUppercase:!1,...n},Array.isArray(e)?e=e.map(o=>o.trim()).filter(o=>o.length).join("-"):e=e.trim(),e.length===0)return"";const t=n.locale===!1?o=>o.toLowerCase():o=>o.toLocaleLowerCase(n.locale),i=n.locale===!1?o=>o.toUpperCase():o=>o.toLocaleUpperCase(n.locale);return e.length===1?m.test(e)?"":n.pascalCase?i(e):t(e):(e!==t(e)&&(e=qe(e,t,i,n.preserveConsecutiveUppercase)),e=e.replace(Ee,""),e=n.preserveConsecutiveUppercase?Re(e,t):t(e),n.pascalCase&&(e=i(e.charAt(0))+e.slice(1)),Ae(e,i))}function Te(e,n,t){const i="virtual:flyo-components",r="\0"+i;return{name:"vite-plugin-flyo-components",async resolveId(o){if(o===i)return r},async load(o){if(o===r){const a=[];for(const[u,c]of Object.entries(n)){const p=await this.resolve("/"+e+"/"+c+".astro");p&&a.push(`export { default as ${Ie(u)} } from "${p.id}"`)}let l=null;return t&&(l=await this.resolve("/"+e+"/"+t+".astro")),l?a.push(`export { default as fallback } from "${l.id}"`):a.push('export { default as fallback } from "@flyo/nitro-astro/FallbackComponent.astro"'),a.join(";")}}}}function P(){return globalThis.flyoNitroInstance||console.error("The Flyo Typescript Configuration has not been initialized correctly"),globalThis.flyoNitroInstance}function f(){return P().config}async function ke(e){return await e.locals.config}function Se(){return new pe(f())}function je(){return new ye(f())}function Le(){return new ve(f())}function Pe(){return new me(f())}function Ue(){return new we(f())}function Fe(){return new _e(f())}function U(e){return{"data-flyo-uid":e.uid}}const $e=U,xe=`
1
+ (function(s,h){typeof exports=="object"&&typeof module<"u"?h(exports):typeof define=="function"&&define.amd?define(["exports"],h):(s=typeof globalThis<"u"?globalThis:s||self,h(s.flyoNitroIntegration={}))})(this,function(s){"use strict";const h="https://api.flyo.cloud/nitro/v1".replace(/\/+$/,"");class ${constructor(n={}){this.configuration=n}set config(n){this.configuration=n}get basePath(){return this.configuration.basePath!=null?this.configuration.basePath:h}get fetchApi(){return this.configuration.fetchApi}get middleware(){return this.configuration.middleware||[]}get queryParamsStringify(){return this.configuration.queryParamsStringify||_}get username(){return this.configuration.username}get password(){return this.configuration.password}get apiKey(){const n=this.configuration.apiKey;if(n)return typeof n=="function"?n:()=>n}get accessToken(){const n=this.configuration.accessToken;if(n)return typeof n=="function"?n:async()=>n}get headers(){return this.configuration.headers}get credentials(){return this.configuration.credentials}}const x=new $,w=class F{constructor(n=x){this.configuration=n,this.fetchApi=async(t,i)=>{let r={url:t,init:i};for(const a of this.middleware)a.pre&&(r=await a.pre({fetch:this.fetchApi,...r})||r);let o;try{o=await(this.configuration.fetchApi||fetch)(r.url,r.init)}catch(a){for(const l of this.middleware)l.onError&&(o=await l.onError({fetch:this.fetchApi,url:r.url,init:r.init,error:a,response:o?o.clone():void 0})||o);if(o===void 0)throw a instanceof Error?new O(a,"The request failed and the interceptors did not return an alternative response"):a}for(const a of this.middleware)a.post&&(o=await a.post({fetch:this.fetchApi,url:r.url,init:r.init,response:o.clone()})||o);return o},this.middleware=n.middleware}withMiddleware(...n){const t=this.clone();return t.middleware=t.middleware.concat(...n),t}withPreMiddleware(...n){const t=n.map(i=>({pre:i}));return this.withMiddleware(...t)}withPostMiddleware(...n){const t=n.map(i=>({post:i}));return this.withMiddleware(...t)}isJsonMime(n){return n?F.jsonRegex.test(n):!1}async request(n,t){const{url:i,init:r}=await this.createFetchParams(n,t),o=await this.fetchApi(i,r);if(o&&o.status>=200&&o.status<300)return o;throw new N(o,"Response returned an error code")}async createFetchParams(n,t){let i=this.configuration.basePath+n.path;n.query!==void 0&&Object.keys(n.query).length!==0&&(i+="?"+this.configuration.queryParamsStringify(n.query));const r=Object.assign({},this.configuration.headers,n.headers);Object.keys(r).forEach(p=>r[p]===void 0?delete r[p]:{});const o=typeof t=="function"?t:async()=>t,a={method:n.method,headers:r,body:n.body,credentials:this.configuration.credentials},l={...a,...await o({init:a,context:n})};let u;M(l.body)||l.body instanceof URLSearchParams||K(l.body)?u=l.body:this.isJsonMime(r["Content-Type"])?u=JSON.stringify(l.body):u=l.body;const c={...l,body:u};return{url:i,init:c}}clone(){const n=this.constructor,t=new n(this.configuration);return t.middleware=this.middleware.slice(),t}};w.jsonRegex=new RegExp("^(:?application/json|[^;/ ]+/[^;/ ]+[+]json)[ ]*(:?;.*)?$","i");let g=w;function K(e){return typeof Blob<"u"&&e instanceof Blob}function M(e){return typeof FormData<"u"&&e instanceof FormData}class N extends Error{constructor(n,t){super(t),this.response=n,this.name="ResponseError"}}class O extends Error{constructor(n,t){super(t),this.cause=n,this.name="FetchError"}}class y extends Error{constructor(n,t){super(t),this.field=n,this.name="RequiredError"}}function _(e,n=""){return Object.keys(e).map(t=>b(t,e[t],n)).filter(t=>t.length>0).join("&")}function b(e,n,t=""){const i=t+(t.length?`[${e}]`:e);if(n instanceof Array){const r=n.map(o=>encodeURIComponent(String(o))).join(`&${encodeURIComponent(i)}=`);return`${encodeURIComponent(i)}=${r}`}if(n instanceof Set){const r=Array.from(n);return b(e,r,t)}return n instanceof Date?`${encodeURIComponent(i)}=${encodeURIComponent(n.toISOString())}`:n instanceof Object?_(n,i):`${encodeURIComponent(i)}=${encodeURIComponent(String(n))}`}function v(e,n){const t={};for(const i of Object.keys(e))t[i]=n(e[i]);return t}class d{constructor(n,t=i=>i){this.raw=n,this.transformer=t}async value(){return this.transformer(await this.raw.json())}}function D(e){return B(e)}function B(e,n){return e==null?e:{identifier:e.identifier==null?void 0:e.identifier,content:e.content==null?void 0:e.content.map(C)}}function C(e){return G(e)}function G(e,n){return e==null?e:{items:e.items==null?void 0:e.items,content:e.content==null?void 0:e.content,config:e.config==null?void 0:e.config,identifier:e.identifier==null?void 0:e.identifier,uid:e.uid==null?void 0:e.uid,component:e.component==null?void 0:e.component,slots:e.slots==null?void 0:v(e.slots,D)}}function E(e){return H(e)}function H(e,n){return e==null?e:{slug:e.slug==null?void 0:e.slug,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href}}function V(e){return z(e)}function z(e,n){return e==null?e:{domain:e.domain==null?void 0:e.domain,slug:e.slug==null?void 0:e.slug,version:e.version==null?void 0:e.version,updated_at:e.updated_at==null?void 0:e.updated_at,language:e.language==null?void 0:e.language,primary_language:e.primary_language==null?void 0:e.primary_language}}function q(e){return J(e)}function J(e,n){return e==null?e:{type:e.type==null?void 0:e.type,target:e.target==null?void 0:e.target,label:e.label==null?void 0:e.label,href:e.href==null?void 0:e.href,slug:e.slug==null?void 0:e.slug,properties:e.properties==null?void 0:e.properties,children:e.children==null?void 0:e.children.map(q)}}function W(e){return Q(e)}function Q(e,n){return e==null?e:{items:e.items==null?void 0:e.items.map(q),uid:e.uid==null?void 0:e.uid,identifier:e.identifier==null?void 0:e.identifier,label:e.label==null?void 0:e.label}}function X(e){return Y(e)}function Y(e,n){return e==null?e:{nitro:e.nitro==null?void 0:V(e.nitro),pages:e.pages==null?void 0:e.pages,containers:e.containers==null?void 0:v(e.containers,W),globals:e.globals==null?void 0:e.globals}}function Z(e){return ee(e)}function ee(e,n){return e==null?e:{shortcode:e.shortcode==null?void 0:e.shortcode,name:e.name==null?void 0:e.name}}function R(e){return ne(e)}function ne(e,n){return e==null?e:{language:e.language==null?void 0:Z(e.language),slug:e.slug==null?void 0:e.slug,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href}}function te(e){return ie(e)}function ie(e,n){return e==null?e:{api:e.api==null?void 0:e.api,image:e.image==null?void 0:e.image}}function oe(e){return re(e)}function re(e,n){return e==null?e:{_version:e._version==null?void 0:e._version,entity_metric:e.entity_metric==null?void 0:te(e.entity_metric),entity_unique_id:e.entity_unique_id==null?void 0:e.entity_unique_id,entity_id:e.entity_id==null?void 0:e.entity_id,entity_image:e.entity_image==null?void 0:e.entity_image,entity_slug:e.entity_slug==null?void 0:e.entity_slug,entity_teaser:e.entity_teaser==null?void 0:e.entity_teaser,entity_time_end:e.entity_time_end==null?void 0:e.entity_time_end,entity_time_start:e.entity_time_start==null?void 0:e.entity_time_start,entity_title:e.entity_title==null?void 0:e.entity_title,entity_type:e.entity_type==null?void 0:e.entity_type,entity_type_id:e.entity_type_id==null?void 0:e.entity_type_id,updated_at:e.updated_at==null?void 0:e.updated_at,routes:e.routes==null?void 0:e.routes}}function A(e){return ae(e)}function ae(e,n){return e==null?e:{entity:e.entity==null?void 0:oe(e.entity),model:e.model==null?void 0:e.model,language:e.language==null?void 0:e.language,jsonld:e.jsonld==null?void 0:e.jsonld,translation:e.translation==null?void 0:e.translation.map(R),breadcrumb:e.breadcrumb==null?void 0:e.breadcrumb.map(E)}}function T(e){return le(e)}function le(e,n){return e==null?e:{entity_unique_id:e.entity_unique_id==null?void 0:e.entity_unique_id,entity_title:e.entity_title==null?void 0:e.entity_title,entity_teaser:e.entity_teaser==null?void 0:e.entity_teaser,entity_slug:e.entity_slug==null?void 0:e.entity_slug,entity_time_start:e.entity_time_start==null?void 0:e.entity_time_start,entity_type:e.entity_type==null?void 0:e.entity_type,entity_type_id:e.entity_type_id==null?void 0:e.entity_type_id,entity_image:e.entity_image==null?void 0:e.entity_image,routes:e.routes==null?void 0:e.routes}}function se(e){return ue(e)}function ue(e,n){return e==null?e:{description:e.description==null?void 0:e.description,image:e.image==null?void 0:e.image,title:e.title==null?void 0:e.title}}function ce(e){return de(e)}function de(e,n){return e==null?e:{value:e.value==null?void 0:e.value,navigation:e.navigation==null?void 0:e.navigation,propagate:e.propagate==null?void 0:e.propagate}}function I(e){return fe(e)}function fe(e,n){return e==null?e:{id:e.id==null?void 0:e.id,title:e.title==null?void 0:e.title,href:e.href==null?void 0:e.href,slug:e.slug==null?void 0:e.slug,json:e.json==null?void 0:e.json.map(C),depth:e.depth==null?void 0:e.depth,is_home:e.is_home==null?void 0:e.is_home,created_at:e.created_at==null?void 0:e.created_at,updated_at:e.updated_at==null?void 0:e.updated_at,is_visible:e.is_visible==null?void 0:e.is_visible,meta_json:e.meta_json==null?void 0:se(e.meta_json),properties:e.properties==null?void 0:v(e.properties,ce),uid:e.uid==null?void 0:e.uid,type:e.type==null?void 0:e.type,target:e.target==null?void 0:e.target,container:e.container==null?void 0:e.container,breadcrumb:e.breadcrumb==null?void 0:e.breadcrumb.map(E),translation:e.translation==null?void 0:e.translation.map(R)}}function ge(e){return he(e)}function he(e,n){return e==null?e:{version:e.version==null?void 0:e.version,updated_at:e.updated_at==null?void 0:e.updated_at}}class pe extends g{async configRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/config",method:"GET",headers:r,query:i},t);return new d(a,l=>X(l))}async config(n={},t){return await(await this.configRaw(n,t)).value()}}class ye extends g{async entityBySlugRaw(n,t){if(n.slug==null)throw new y("slug",'Required parameter "slug" was null or undefined when calling entityBySlug().');const i={};n.typeId!=null&&(i.typeId=n.typeId),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));let o="/entities/slug/{slug}";o=o.replace("{slug}",encodeURIComponent(String(n.slug)));const a=await this.request({path:o,method:"GET",headers:r,query:i},t);return new d(a,l=>A(l))}async entityBySlug(n,t){return await(await this.entityBySlugRaw(n,t)).value()}async entityByUniqueidRaw(n,t){if(n.uniqueid==null)throw new y("uniqueid",'Required parameter "uniqueid" was null or undefined when calling entityByUniqueid().');const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));let o="/entities/uniqueid/{uniqueid}";o=o.replace("{uniqueid}",encodeURIComponent(String(n.uniqueid)));const a=await this.request({path:o,method:"GET",headers:r,query:i},t);return new d(a,l=>A(l))}async entityByUniqueid(n,t){return await(await this.entityByUniqueidRaw(n,t)).value()}}class ve extends g{async homeRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/pages/home",method:"GET",headers:r,query:i},t);return new d(a,l=>I(l))}async home(n={},t){return await(await this.homeRaw(n,t)).value()}async pageRaw(n,t){const i={};n.slug!=null&&(i.slug=n.slug),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/pages",method:"GET",headers:r,query:i},t);return new d(a,l=>I(l))}async page(n={},t){return await(await this.pageRaw(n,t)).value()}}class me extends g{async searchRaw(n,t){if(n.query==null)throw new y("query",'Required parameter "query" was null or undefined when calling search().');const i={};n.query!=null&&(i.query=n.query),n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/search",method:"GET",headers:r,query:i},t);return new d(a,l=>l.map(T))}async search(n,t){return await(await this.searchRaw(n,t)).value()}}class we extends g{async sitemapRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/sitemap",method:"GET",headers:r,query:i},t);return new d(a,l=>l.map(T))}async sitemap(n={},t){return await(await this.sitemapRaw(n,t)).value()}}class _e extends g{async versionRaw(n,t){const i={};n.lang!=null&&(i.lang=n.lang);const r={};this.configuration&&this.configuration.apiKey&&(i.token=await this.configuration.apiKey("token"));const a=await this.request({path:"/version",method:"GET",headers:r,query:i},t);return new d(a,l=>ge(l))}async version(n={},t){return await(await this.versionRaw(n,t)).value()}}const be=/[\p{Lu}]/u,Ce=/[\p{Ll}]/u,k=/^[\p{Lu}](?![\p{Lu}])/gu,S=/([\p{Alpha}\p{N}_]|$)/u,m=/[_.\- ]+/,Ee=new RegExp("^"+m.source),j=new RegExp(m.source+S.source,"gu"),L=new RegExp("\\d+"+S.source,"gu"),qe=(e,n,t,i)=>{let r=!1,o=!1,a=!1,l=!1;for(let u=0;u<e.length;u++){const c=e[u];l=u>2?e[u-3]==="-":!0,r&&be.test(c)?(e=e.slice(0,u)+"-"+e.slice(u),r=!1,a=o,o=!0,u++):o&&a&&Ce.test(c)&&(!l||i)?(e=e.slice(0,u-1)+"-"+e.slice(u-1),a=o,o=!1,r=!0):(r=n(c)===c&&t(c)!==c,a=o,o=t(c)===c&&n(c)!==c)}return e},Re=(e,n)=>(k.lastIndex=0,e.replaceAll(k,t=>n(t))),Ae=(e,n)=>(j.lastIndex=0,L.lastIndex=0,e.replaceAll(L,(t,i,r)=>["_","-"].includes(e.charAt(r+t.length))?t:n(t)).replaceAll(j,(t,i)=>n(i)));function Te(e,n){if(!(typeof e=="string"||Array.isArray(e)))throw new TypeError("Expected the input to be `string | string[]`");if(n={pascalCase:!1,preserveConsecutiveUppercase:!1,...n},Array.isArray(e)?e=e.map(o=>o.trim()).filter(o=>o.length).join("-"):e=e.trim(),e.length===0)return"";const t=n.locale===!1?o=>o.toLowerCase():o=>o.toLocaleLowerCase(n.locale),i=n.locale===!1?o=>o.toUpperCase():o=>o.toLocaleUpperCase(n.locale);return e.length===1?m.test(e)?"":n.pascalCase?i(e):t(e):(e!==t(e)&&(e=qe(e,t,i,n.preserveConsecutiveUppercase)),e=e.replace(Ee,""),e=n.preserveConsecutiveUppercase?Re(e,t):t(e),n.pascalCase&&(e=i(e.charAt(0))+e.slice(1)),Ae(e,i))}function Ie(e,n,t){const i="virtual:flyo-components",r="\0"+i;return{name:"vite-plugin-flyo-components",async resolveId(o){if(o===i)return r},async load(o){if(o===r){const a=[];for(const[u,c]of Object.entries(n)){const p=await this.resolve("/"+e+"/"+c+".astro");p&&a.push(`export { default as ${Te(u)} } from "${p.id}"`)}let l=null;return t&&(l=await this.resolve("/"+e+"/"+t+".astro")),l?a.push(`export { default as fallback } from "${l.id}"`):a.push('export { default as fallback } from "@flyo/nitro-astro/FallbackComponent.astro"'),a.join(";")}}}}function P(){return globalThis.flyoNitroInstance||console.error("The Flyo Typescript Configuration has not been initialized correctly"),globalThis.flyoNitroInstance}function f(){return P().config}async function ke(e){return await e.locals.config}function Se(){return new pe(f())}function je(){return new ye(f())}function Le(){return new ve(f())}function Pe(){return new me(f())}function Ue(){return new we(f())}function Fe(){return new _e(f())}function U(e){return{"data-flyo-uid":e.uid}}const $e=U,xe=`
2
2
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 163.4 88.5">
3
3
  <style type="text/css">
4
4
  .st8{fill:#FFFFFF;}
@@ -29,7 +29,7 @@
29
29
  </g>
30
30
  </g>
31
31
  </svg>
32
- `;function Ke(e){const n={accessToken:!1,liveEdit:!1,fallbackComponent:null,componentsDir:"src/components/flyo",serverCacheHeaderTtl:1200,clientCacheHeaderTtl:900,...e};return n.liveEdit==="true"?n.liveEdit=!0:n.liveEdit==="false"&&(n.liveEdit=!1),{name:"@flyo/nitro-astro",hooks:{"astro:config:setup":({injectScript:t,updateConfig:i,injectRoute:r,addMiddleware:o,addDevToolbarApp:a})=>{if(!n.accessToken||n.accessToken.length==0)throw new Error("The Flyo Nitro Integration requires an accessToken");a({id:"flyo-nitro",name:"Flyo Nitro",icon:xe,entrypoint:"@flyo/nitro-astro/toolbar.ts"}),r({pattern:"sitemap.xml",entrypoint:"@flyo/nitro-astro/sitemap.ts"}),o({entrypoint:"@flyo/nitro-astro/middleware.ts",order:"post"}),i({image:{service:{entrypoint:"@flyo/nitro-astro/cdn.ts"}},vite:{plugins:[Te(n.componentsDir,n.components||{},n.fallbackComponent)]}}),t("page-ssr",`
32
+ `;function Ke(e){const n={accessToken:!1,liveEdit:!1,fallbackComponent:null,componentsDir:"src/components/flyo",serverCacheHeaderTtl:1200,clientCacheHeaderTtl:900,...e};return n.liveEdit==="true"?n.liveEdit=!0:n.liveEdit==="false"&&(n.liveEdit=!1),{name:"@flyo/nitro-astro",hooks:{"astro:config:setup":({injectScript:t,updateConfig:i,injectRoute:r,addMiddleware:o,addDevToolbarApp:a})=>{if(!n.accessToken||n.accessToken.length==0)throw new Error("The Flyo Nitro Integration requires an accessToken");a({id:"flyo-nitro",name:"Flyo Nitro",icon:xe,entrypoint:"@flyo/nitro-astro/toolbar.ts"}),r({pattern:"sitemap.xml",entrypoint:"@flyo/nitro-astro/sitemap.ts"}),o({entrypoint:"@flyo/nitro-astro/middleware.ts",order:"post"}),i({image:{service:{entrypoint:"@flyo/nitro-astro/cdn.ts"}},vite:{plugins:[Ie(n.componentsDir,n.components||{},n.fallbackComponent)]}}),t("page-ssr",`
33
33
  import { Configuration } from '@flyo/nitro-typescript'
34
34
 
35
35
  var defaultConfig = new Configuration({
@@ -39,6 +39,7 @@
39
39
  globalThis.flyoNitroInstance = {
40
40
  config: defaultConfig,
41
41
  options: {
42
+ accessToken: '${n.accessToken}',
42
43
  liveEdit: ${n.liveEdit},
43
44
  componentsDir: '${n.componentsDir}',
44
45
  clientCacheHeaderTtl: ${n.clientCacheHeaderTtl},
@@ -215,7 +215,7 @@ function O(e, n) {
215
215
  slots: e.slots == null ? void 0 : p(e.slots, N)
216
216
  };
217
217
  }
218
- function I(e) {
218
+ function k(e) {
219
219
  return D(e);
220
220
  }
221
221
  function D(e, n) {
@@ -238,7 +238,7 @@ function G(e, n) {
238
238
  primary_language: e.primary_language == null ? void 0 : e.primary_language
239
239
  };
240
240
  }
241
- function k(e) {
241
+ function I(e) {
242
242
  return H(e);
243
243
  }
244
244
  function H(e, n) {
@@ -249,7 +249,7 @@ function H(e, n) {
249
249
  href: e.href == null ? void 0 : e.href,
250
250
  slug: e.slug == null ? void 0 : e.slug,
251
251
  properties: e.properties == null ? void 0 : e.properties,
252
- children: e.children == null ? void 0 : e.children.map(k)
252
+ children: e.children == null ? void 0 : e.children.map(I)
253
253
  };
254
254
  }
255
255
  function z(e) {
@@ -257,7 +257,7 @@ function z(e) {
257
257
  }
258
258
  function V(e, n) {
259
259
  return e == null ? e : {
260
- items: e.items == null ? void 0 : e.items.map(k),
260
+ items: e.items == null ? void 0 : e.items.map(I),
261
261
  uid: e.uid == null ? void 0 : e.uid,
262
262
  identifier: e.identifier == null ? void 0 : e.identifier,
263
263
  label: e.label == null ? void 0 : e.label
@@ -334,7 +334,7 @@ function ie(e, n) {
334
334
  language: e.language == null ? void 0 : e.language,
335
335
  jsonld: e.jsonld == null ? void 0 : e.jsonld,
336
336
  translation: e.translation == null ? void 0 : e.translation.map(T),
337
- breadcrumb: e.breadcrumb == null ? void 0 : e.breadcrumb.map(I)
337
+ breadcrumb: e.breadcrumb == null ? void 0 : e.breadcrumb.map(k)
338
338
  };
339
339
  }
340
340
  function x(e) {
@@ -394,7 +394,7 @@ function ue(e, n) {
394
394
  type: e.type == null ? void 0 : e.type,
395
395
  target: e.target == null ? void 0 : e.target,
396
396
  container: e.container == null ? void 0 : e.container,
397
- breadcrumb: e.breadcrumb == null ? void 0 : e.breadcrumb.map(I),
397
+ breadcrumb: e.breadcrumb == null ? void 0 : e.breadcrumb.map(k),
398
398
  translation: e.translation == null ? void 0 : e.translation.map(T)
399
399
  };
400
400
  }
@@ -712,12 +712,12 @@ function Ue() {
712
712
  function Pe() {
713
713
  return new ve(f());
714
714
  }
715
- function Ie(e) {
715
+ function ke(e) {
716
716
  return {
717
717
  "data-flyo-uid": e.uid
718
718
  };
719
719
  }
720
- const Fe = Ie, ke = `
720
+ const Fe = ke, Ie = `
721
721
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 163.4 88.5">
722
722
  <style type="text/css">
723
723
  .st8{fill:#FFFFFF;}
@@ -777,7 +777,7 @@ function $e(e) {
777
777
  id: "flyo-nitro",
778
778
  name: "Flyo Nitro",
779
779
  //icon: 'lightbulb',
780
- icon: ke,
780
+ icon: Ie,
781
781
  //icon: '<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"><ellipse cx="100" cy="60" rx="90" ry="50" fill="#F4A460" /><circle cx="70" cy="45" r="5" fill="white" /><circle cx="90" cy="30" r="5" fill="white" /><circle cx="110" cy="45" r="5" fill="white" /><circle cx="130" cy="30" r="5" fill="white" /><circle cx="150" cy="45" r="5" fill="white" /><path d="M30 90 Q60 75, 90 90 T150 90 Q160 80, 180 90" fill="#228B22" /><rect x="30" y="90" width="140" height="15" fill="#FF6347" /><rect x="30" y="105" width="140" height="15" fill="#FFD700" /><rect x="30" y="120" width="140" height="25" fill="#8B4513" /><ellipse cx="100" cy="160" rx="90" ry="30" fill="#F4A460" /></svg>',
782
782
  entrypoint: "@flyo/nitro-astro/toolbar.ts"
783
783
  }), r({
@@ -813,6 +813,7 @@ function $e(e) {
813
813
  globalThis.flyoNitroInstance = {
814
814
  config: defaultConfig,
815
815
  options: {
816
+ accessToken: '${n.accessToken}',
816
817
  liveEdit: ${n.liveEdit},
817
818
  componentsDir: '${n.componentsDir}',
818
819
  clientCacheHeaderTtl: ${n.clientCacheHeaderTtl},
@@ -852,7 +853,7 @@ function $e(e) {
852
853
  }
853
854
  export {
854
855
  $e as default,
855
- Ie as editable,
856
+ ke as editable,
856
857
  Fe as editableBlock,
857
858
  Te as useConfig,
858
859
  xe as useConfigApi,
@@ -0,0 +1,3 @@
1
+ import { default as DebugInfo } from './DebugInfo.astro';
2
+
3
+ export default DebugInfo;
@@ -0,0 +1 @@
1
+ export {};
@@ -53,6 +53,7 @@ export interface IntegrationOptions {
53
53
  export interface FlyoIntegration {
54
54
  config: Configuration;
55
55
  options: {
56
+ accessToken: string;
56
57
  liveEdit: boolean;
57
58
  componentsDir: string;
58
59
  clientCacheHeaderTtl: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flyo/nitro-astro",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Connecting Flyo Headless Content Hub into your Astro project.",
5
5
  "homepage": "https://dev.flyo.cloud/nitro",
6
6
  "keywords": [
@@ -73,6 +73,11 @@
73
73
  "import": "./components/FlyoWysiwyg.ts",
74
74
  "require": "./components/FlyoWysiwyg.ts"
75
75
  },
76
+ "./DebugInfo.astro": {
77
+ "types": "./components/DebugInfo.ts",
78
+ "import": "./components/DebugInfo.ts",
79
+ "require": "./components/DebugInfo.ts"
80
+ },
76
81
  "./cdn.ts": "./cdn.ts",
77
82
  "./middleware.ts": "./middleware.ts",
78
83
  "./sitemap.ts": "./sitemap.ts",