@webstudio-is/sdk 0.145.0 → 0.151.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -71,8 +71,7 @@ var commonPageFields = {
71
71
  ).optional()
72
72
  }),
73
73
  rootInstanceId: z2.string(),
74
- // @todo make required after releasing migration
75
- systemDataSourceId: z2.optional(z2.string())
74
+ systemDataSourceId: z2.string()
76
75
  };
77
76
  var HomePagePath = z2.string().refine((path) => path === "", "Home page path must be empty");
78
77
  var HomePage = z2.object({
@@ -99,11 +98,15 @@ var Page = z2.object({
99
98
  var ProjectMeta = z2.object({
100
99
  // All fields are optional to ensure consistency and allow for the addition of new fields without requiring migration
101
100
  siteName: z2.string().optional(),
101
+ contactEmail: z2.string().optional(),
102
102
  faviconAssetId: z2.string().optional(),
103
103
  code: z2.string().optional()
104
104
  });
105
105
  var ProjectNewRedirectPath = PagePath.or(
106
106
  z2.string().refine((data) => {
107
+ if (data === "/") {
108
+ return true;
109
+ }
107
110
  try {
108
111
  new URL(data);
109
112
  return true;
@@ -238,6 +241,8 @@ var ResourceRequest = z5.object({
238
241
  body: z5.optional(z5.unknown())
239
242
  });
240
243
  var Resources = z5.map(ResourceId, Resource);
244
+ var LOCAL_RESOURCE_PREFIX = "$resources";
245
+ var isLocalResource = (pathname, resourceName) => pathname.split("/").filter(Boolean).join("/") === `${LOCAL_RESOURCE_PREFIX}/${resourceName}`;
241
246
 
242
247
  // src/schema/props.ts
243
248
  import { z as z6 } from "zod";
@@ -451,133 +456,6 @@ var parseComponentName = (componentName) => {
451
456
  return [namespace, name];
452
457
  };
453
458
 
454
- // src/page-utils.ts
455
- var ROOT_FOLDER_ID = "root";
456
- var isRoot = (folder) => folder.id === ROOT_FOLDER_ID;
457
- var findPageByIdOrPath = (idOrPath, pages) => {
458
- if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePage.id) {
459
- return pages.homePage;
460
- }
461
- return pages.pages.find(
462
- (page) => page.id === idOrPath || getPagePath(page.id, pages) === idOrPath
463
- );
464
- };
465
- var findParentFolderByChildId = (id, folders) => {
466
- for (const folder of folders) {
467
- if (folder.children.includes(id)) {
468
- return folder;
469
- }
470
- }
471
- };
472
- var getPagePath = (id, pages) => {
473
- const foldersMap = /* @__PURE__ */ new Map();
474
- const childParentMap = /* @__PURE__ */ new Map();
475
- for (const folder of pages.folders) {
476
- foldersMap.set(folder.id, folder);
477
- for (const childId of folder.children) {
478
- childParentMap.set(childId, folder.id);
479
- }
480
- }
481
- const paths = [];
482
- let currentId = id;
483
- const allPages = [pages.homePage, ...pages.pages];
484
- for (const page of allPages) {
485
- if (page.id === id) {
486
- paths.push(page.path);
487
- currentId = childParentMap.get(page.id);
488
- break;
489
- }
490
- }
491
- while (currentId) {
492
- const folder = foldersMap.get(currentId);
493
- if (folder === void 0) {
494
- break;
495
- }
496
- paths.push(folder.slug);
497
- currentId = childParentMap.get(currentId);
498
- }
499
- return paths.reverse().join("/").replace(/\/+/g, "/");
500
- };
501
-
502
- // src/scope.ts
503
- var normalizeName = (name) => {
504
- name = name.replaceAll(/[^\w$]/g, "");
505
- if (name.length === 0) {
506
- return "_";
507
- }
508
- if (/[A-Za-z_$]/.test(name[0]) === false) {
509
- name = `_${name}`;
510
- }
511
- return name;
512
- };
513
- var createScope = (occupiedIdentifiers = []) => {
514
- const freeIndexByPreferredName = /* @__PURE__ */ new Map();
515
- const scopedNameByIdMap = /* @__PURE__ */ new Map();
516
- for (const identifier of occupiedIdentifiers) {
517
- freeIndexByPreferredName.set(identifier, 1);
518
- }
519
- const getName = (id, preferredName) => {
520
- const cachedName = scopedNameByIdMap.get(id);
521
- if (cachedName !== void 0) {
522
- return cachedName;
523
- }
524
- preferredName = normalizeName(preferredName);
525
- const index = freeIndexByPreferredName.get(preferredName);
526
- freeIndexByPreferredName.set(preferredName, (index ?? 0) + 1);
527
- let scopedName = preferredName;
528
- if (index !== void 0) {
529
- scopedName = `${preferredName}_${index}`;
530
- }
531
- scopedNameByIdMap.set(id, scopedName);
532
- return scopedName;
533
- };
534
- return {
535
- getName
536
- };
537
- };
538
-
539
- // src/resource-loader.ts
540
- var loadResource = async (resourceData) => {
541
- const { url, method, headers, body } = resourceData;
542
- const requestHeaders = new Headers(
543
- headers.map(({ name, value }) => [name, value])
544
- );
545
- const requestInit = {
546
- method,
547
- headers: requestHeaders
548
- };
549
- if (method !== "get" && body !== void 0) {
550
- if (typeof body === "string") {
551
- requestInit.body = body;
552
- }
553
- if (typeof body === "object") {
554
- requestInit.body = JSON.stringify(body);
555
- }
556
- }
557
- try {
558
- const response = await fetch(url.trim(), requestInit);
559
- let data;
560
- if (response.ok && // accept json by default and when specified explicitly
561
- (requestHeaders.has("accept") === false || requestHeaders.get("accept") === "application/json")) {
562
- data = await response.json();
563
- } else {
564
- data = await response.text();
565
- }
566
- return {
567
- data,
568
- status: response.status,
569
- statusText: response.statusText
570
- };
571
- } catch (error) {
572
- const message = error.message;
573
- return {
574
- data: void 0,
575
- status: 500,
576
- statusText: message
577
- };
578
- }
579
- };
580
-
581
459
  // src/expression.ts
582
460
  import { parseExpressionAt } from "acorn";
583
461
  import { simple } from "acorn-walk";
@@ -816,6 +694,142 @@ var executeExpression = (expression) => {
816
694
  }
817
695
  };
818
696
 
697
+ // src/page-utils.ts
698
+ var ROOT_FOLDER_ID = "root";
699
+ var isRoot = (folder) => folder.id === ROOT_FOLDER_ID;
700
+ var findPageByIdOrPath = (idOrPath, pages) => {
701
+ if (idOrPath === "" || idOrPath === "/" || idOrPath === pages.homePage.id) {
702
+ return pages.homePage;
703
+ }
704
+ return pages.pages.find(
705
+ (page) => page.id === idOrPath || getPagePath(page.id, pages) === idOrPath
706
+ );
707
+ };
708
+ var findParentFolderByChildId = (id, folders) => {
709
+ for (const folder of folders) {
710
+ if (folder.children.includes(id)) {
711
+ return folder;
712
+ }
713
+ }
714
+ };
715
+ var getPagePath = (id, pages) => {
716
+ const foldersMap = /* @__PURE__ */ new Map();
717
+ const childParentMap = /* @__PURE__ */ new Map();
718
+ for (const folder of pages.folders) {
719
+ foldersMap.set(folder.id, folder);
720
+ for (const childId of folder.children) {
721
+ childParentMap.set(childId, folder.id);
722
+ }
723
+ }
724
+ const paths = [];
725
+ let currentId = id;
726
+ const allPages = [pages.homePage, ...pages.pages];
727
+ for (const page of allPages) {
728
+ if (page.id === id) {
729
+ paths.push(page.path);
730
+ currentId = childParentMap.get(page.id);
731
+ break;
732
+ }
733
+ }
734
+ while (currentId) {
735
+ const folder = foldersMap.get(currentId);
736
+ if (folder === void 0) {
737
+ break;
738
+ }
739
+ paths.push(folder.slug);
740
+ currentId = childParentMap.get(currentId);
741
+ }
742
+ return paths.reverse().join("/").replace(/\/+/g, "/");
743
+ };
744
+ var getStaticSiteMapXml = (pages, updatedAt) => {
745
+ const allPages = [pages.homePage, ...pages.pages];
746
+ return allPages.filter(
747
+ (page) => executeExpression(page.meta.excludePageFromSearch) !== true
748
+ ).map((page) => ({
749
+ path: getPagePath(page.id, pages),
750
+ lastModified: updatedAt.split("T")[0]
751
+ }));
752
+ };
753
+
754
+ // src/scope.ts
755
+ var normalizeName = (name) => {
756
+ name = name.replaceAll(/[^\w$]/g, "");
757
+ if (name.length === 0) {
758
+ return "_";
759
+ }
760
+ if (/[A-Za-z_$]/.test(name[0]) === false) {
761
+ name = `_${name}`;
762
+ }
763
+ return name;
764
+ };
765
+ var createScope = (occupiedIdentifiers = []) => {
766
+ const freeIndexByPreferredName = /* @__PURE__ */ new Map();
767
+ const scopedNameByIdMap = /* @__PURE__ */ new Map();
768
+ for (const identifier of occupiedIdentifiers) {
769
+ freeIndexByPreferredName.set(identifier, 1);
770
+ }
771
+ const getName = (id, preferredName) => {
772
+ const cachedName = scopedNameByIdMap.get(id);
773
+ if (cachedName !== void 0) {
774
+ return cachedName;
775
+ }
776
+ preferredName = normalizeName(preferredName);
777
+ const index = freeIndexByPreferredName.get(preferredName);
778
+ freeIndexByPreferredName.set(preferredName, (index ?? 0) + 1);
779
+ let scopedName = preferredName;
780
+ if (index !== void 0) {
781
+ scopedName = `${preferredName}_${index}`;
782
+ }
783
+ scopedNameByIdMap.set(id, scopedName);
784
+ return scopedName;
785
+ };
786
+ return {
787
+ getName
788
+ };
789
+ };
790
+
791
+ // src/resource-loader.ts
792
+ var loadResource = async (customFetch, resourceData) => {
793
+ const { url, method, headers, body } = resourceData;
794
+ const requestHeaders = new Headers(
795
+ headers.map(({ name, value }) => [name, value])
796
+ );
797
+ const requestInit = {
798
+ method,
799
+ headers: requestHeaders
800
+ };
801
+ if (method !== "get" && body !== void 0) {
802
+ if (typeof body === "string") {
803
+ requestInit.body = body;
804
+ }
805
+ if (typeof body === "object") {
806
+ requestInit.body = JSON.stringify(body);
807
+ }
808
+ }
809
+ try {
810
+ const response = await customFetch(url.trim(), requestInit);
811
+ let data;
812
+ if (response.ok && // accept json by default and when specified explicitly
813
+ (requestHeaders.has("accept") === false || requestHeaders.get("accept") === "application/json")) {
814
+ data = await response.json();
815
+ } else {
816
+ data = await response.text();
817
+ }
818
+ return {
819
+ data,
820
+ status: response.status,
821
+ statusText: response.statusText
822
+ };
823
+ } catch (error) {
824
+ const message = error.message;
825
+ return {
826
+ data: void 0,
827
+ status: 500,
828
+ statusText: message
829
+ };
830
+ }
831
+ };
832
+
819
833
  // src/forms-generator.ts
820
834
  var generateFormsProperties = (props) => {
821
835
  const formsProperties = /* @__PURE__ */ new Map();
@@ -861,7 +875,7 @@ var generateResourcesLoader = ({
861
875
  const resourceName = scope.getName(resource.id, dataSource.name);
862
876
  generatedOutput += `${resourceName},
863
877
  `;
864
- generatedLoaders += `loadResource({
878
+ generatedLoaders += `loadResource(customFetch, {
865
879
  `;
866
880
  generatedLoaders += `id: "${resource.id}",
867
881
  `;
@@ -923,12 +937,32 @@ var generateResourcesLoader = ({
923
937
  }
924
938
  }
925
939
  let generated = "";
926
- generated += `import { loadResource, type System } from "@webstudio-is/sdk";
940
+ generated += `import { loadResource, isLocalResource, type System } from "@webstudio-is/sdk";
927
941
  `;
942
+ if (hasResources) {
943
+ generated += `import { sitemap } from "./[sitemap.xml]";
944
+ `;
945
+ }
928
946
  generated += `export const loadResources = async (_props: { system: System }) => {
929
947
  `;
930
948
  generated += generatedVariables;
931
949
  if (hasResources) {
950
+ generated += `
951
+ const customFetch: typeof fetch = (input, init) => {
952
+ if (typeof input !== "string") {
953
+ return fetch(input, init);
954
+ }
955
+
956
+ if (isLocalResource(input, "sitemap.xml")) {
957
+ // @todo: dynamic import sitemap ???
958
+ const response = new Response(JSON.stringify(sitemap));
959
+ response.headers.set('content-type', 'application/json; charset=utf-8');
960
+ return Promise.resolve(response);
961
+ }
962
+
963
+ return fetch(input, init);
964
+ };
965
+ `;
932
966
  generated += `const [
933
967
  `;
934
968
  generated += generatedOutput;
@@ -1148,9 +1182,11 @@ export {
1148
1182
  generateResourcesLoader,
1149
1183
  getExpressionIdentifiers,
1150
1184
  getPagePath,
1185
+ getStaticSiteMapXml,
1151
1186
  getStyleDeclKey,
1152
1187
  initialBreakpoints,
1153
1188
  isLiteralExpression,
1189
+ isLocalResource,
1154
1190
  isRoot,
1155
1191
  lintExpression,
1156
1192
  loadResource,
@@ -16,3 +16,7 @@ export declare const findParentFolderByChildId: (id: Folder["id"] | Page["id"],
16
16
  * Get a path from all folder slugs from root to the current folder or page.
17
17
  */
18
18
  export declare const getPagePath: (id: Folder["id"] | Page["id"], pages: Pages) => string;
19
+ export declare const getStaticSiteMapXml: (pages: Pages, updatedAt: string) => {
20
+ path: string;
21
+ lastModified: string;
22
+ }[];
@@ -1,5 +1,5 @@
1
1
  import type { ResourceRequest } from "./schema/resources";
2
- export declare const loadResource: (resourceData: ResourceRequest) => Promise<{
2
+ export declare const loadResource: (customFetch: typeof fetch, resourceData: ResourceRequest) => Promise<{
3
3
  data: any;
4
4
  status: number;
5
5
  statusText: string;
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  export type System = {
3
3
  params: Record<string, string | undefined>;
4
4
  search: Record<string, string | undefined>;
5
+ origin: string;
5
6
  };
6
7
  export declare const FolderName: z.ZodEffects<z.ZodString, string, string>;
7
8
  export declare const Folder: z.ZodObject<{
@@ -77,7 +78,7 @@ declare const Page: z.ZodObject<{
77
78
  }[] | undefined;
78
79
  }>;
79
80
  rootInstanceId: z.ZodString;
80
- systemDataSourceId: z.ZodOptional<z.ZodString>;
81
+ systemDataSourceId: z.ZodString;
81
82
  }, "strip", z.ZodTypeAny, {
82
83
  path: string;
83
84
  id: string;
@@ -98,7 +99,7 @@ declare const Page: z.ZodObject<{
98
99
  };
99
100
  title: string;
100
101
  rootInstanceId: string;
101
- systemDataSourceId?: string | undefined;
102
+ systemDataSourceId: string;
102
103
  }, {
103
104
  path: string;
104
105
  id: string;
@@ -119,18 +120,21 @@ declare const Page: z.ZodObject<{
119
120
  };
120
121
  title: string;
121
122
  rootInstanceId: string;
122
- systemDataSourceId?: string | undefined;
123
+ systemDataSourceId: string;
123
124
  }>;
124
125
  declare const ProjectMeta: z.ZodObject<{
125
126
  siteName: z.ZodOptional<z.ZodString>;
127
+ contactEmail: z.ZodOptional<z.ZodString>;
126
128
  faviconAssetId: z.ZodOptional<z.ZodString>;
127
129
  code: z.ZodOptional<z.ZodString>;
128
130
  }, "strip", z.ZodTypeAny, {
129
131
  siteName?: string | undefined;
132
+ contactEmail?: string | undefined;
130
133
  faviconAssetId?: string | undefined;
131
134
  code?: string | undefined;
132
135
  }, {
133
136
  siteName?: string | undefined;
137
+ contactEmail?: string | undefined;
134
138
  faviconAssetId?: string | undefined;
135
139
  code?: string | undefined;
136
140
  }>;
@@ -162,14 +166,17 @@ export type Page = z.infer<typeof Page>;
162
166
  export declare const Pages: z.ZodObject<{
163
167
  meta: z.ZodOptional<z.ZodObject<{
164
168
  siteName: z.ZodOptional<z.ZodString>;
169
+ contactEmail: z.ZodOptional<z.ZodString>;
165
170
  faviconAssetId: z.ZodOptional<z.ZodString>;
166
171
  code: z.ZodOptional<z.ZodString>;
167
172
  }, "strip", z.ZodTypeAny, {
168
173
  siteName?: string | undefined;
174
+ contactEmail?: string | undefined;
169
175
  faviconAssetId?: string | undefined;
170
176
  code?: string | undefined;
171
177
  }, {
172
178
  siteName?: string | undefined;
179
+ contactEmail?: string | undefined;
173
180
  faviconAssetId?: string | undefined;
174
181
  code?: string | undefined;
175
182
  }>>;
@@ -245,7 +252,7 @@ export declare const Pages: z.ZodObject<{
245
252
  }[] | undefined;
246
253
  }>;
247
254
  rootInstanceId: z.ZodString;
248
- systemDataSourceId: z.ZodOptional<z.ZodString>;
255
+ systemDataSourceId: z.ZodString;
249
256
  }, "strip", z.ZodTypeAny, {
250
257
  path: string;
251
258
  id: string;
@@ -266,7 +273,7 @@ export declare const Pages: z.ZodObject<{
266
273
  };
267
274
  title: string;
268
275
  rootInstanceId: string;
269
- systemDataSourceId?: string | undefined;
276
+ systemDataSourceId: string;
270
277
  }, {
271
278
  path: string;
272
279
  id: string;
@@ -287,7 +294,7 @@ export declare const Pages: z.ZodObject<{
287
294
  };
288
295
  title: string;
289
296
  rootInstanceId: string;
290
- systemDataSourceId?: string | undefined;
297
+ systemDataSourceId: string;
291
298
  }>;
292
299
  pages: z.ZodArray<z.ZodObject<{
293
300
  path: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>, string, string>, string, string>, string, string>, string, string>, string, string>;
@@ -341,7 +348,7 @@ export declare const Pages: z.ZodObject<{
341
348
  }[] | undefined;
342
349
  }>;
343
350
  rootInstanceId: z.ZodString;
344
- systemDataSourceId: z.ZodOptional<z.ZodString>;
351
+ systemDataSourceId: z.ZodString;
345
352
  }, "strip", z.ZodTypeAny, {
346
353
  path: string;
347
354
  id: string;
@@ -362,7 +369,7 @@ export declare const Pages: z.ZodObject<{
362
369
  };
363
370
  title: string;
364
371
  rootInstanceId: string;
365
- systemDataSourceId?: string | undefined;
372
+ systemDataSourceId: string;
366
373
  }, {
367
374
  path: string;
368
375
  id: string;
@@ -383,7 +390,7 @@ export declare const Pages: z.ZodObject<{
383
390
  };
384
391
  title: string;
385
392
  rootInstanceId: string;
386
- systemDataSourceId?: string | undefined;
393
+ systemDataSourceId: string;
387
394
  }>, "many">;
388
395
  folders: z.ZodEffects<z.ZodArray<z.ZodObject<{
389
396
  id: z.ZodString;
@@ -432,7 +439,7 @@ export declare const Pages: z.ZodObject<{
432
439
  };
433
440
  title: string;
434
441
  rootInstanceId: string;
435
- systemDataSourceId?: string | undefined;
442
+ systemDataSourceId: string;
436
443
  };
437
444
  pages: {
438
445
  path: string;
@@ -454,7 +461,7 @@ export declare const Pages: z.ZodObject<{
454
461
  };
455
462
  title: string;
456
463
  rootInstanceId: string;
457
- systemDataSourceId?: string | undefined;
464
+ systemDataSourceId: string;
458
465
  }[];
459
466
  folders: {
460
467
  id: string;
@@ -464,6 +471,7 @@ export declare const Pages: z.ZodObject<{
464
471
  }[];
465
472
  meta?: {
466
473
  siteName?: string | undefined;
474
+ contactEmail?: string | undefined;
467
475
  faviconAssetId?: string | undefined;
468
476
  code?: string | undefined;
469
477
  } | undefined;
@@ -496,7 +504,7 @@ export declare const Pages: z.ZodObject<{
496
504
  };
497
505
  title: string;
498
506
  rootInstanceId: string;
499
- systemDataSourceId?: string | undefined;
507
+ systemDataSourceId: string;
500
508
  };
501
509
  pages: {
502
510
  path: string;
@@ -518,7 +526,7 @@ export declare const Pages: z.ZodObject<{
518
526
  };
519
527
  title: string;
520
528
  rootInstanceId: string;
521
- systemDataSourceId?: string | undefined;
529
+ systemDataSourceId: string;
522
530
  }[];
523
531
  folders: {
524
532
  id: string;
@@ -528,6 +536,7 @@ export declare const Pages: z.ZodObject<{
528
536
  }[];
529
537
  meta?: {
530
538
  siteName?: string | undefined;
539
+ contactEmail?: string | undefined;
531
540
  faviconAssetId?: string | undefined;
532
541
  code?: string | undefined;
533
542
  } | undefined;
@@ -113,3 +113,7 @@ export declare const Resources: z.ZodMap<z.ZodString, z.ZodObject<{
113
113
  body?: string | undefined;
114
114
  }>>;
115
115
  export type Resources = z.infer<typeof Resources>;
116
+ /**
117
+ * Prevents fetch cycles by prefixing local resources.
118
+ */
119
+ export declare const isLocalResource: (pathname: string, resourceName: string) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/sdk",
3
- "version": "0.145.0",
3
+ "version": "0.151.0",
4
4
  "description": "Webstudio project data schema",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -22,8 +22,8 @@
22
22
  "acorn-walk": "^8.3.2",
23
23
  "type-fest": "^4.3.1",
24
24
  "zod": "^3.22.4",
25
- "@webstudio-is/css-engine": "0.145.0",
26
- "@webstudio-is/fonts": "0.145.0"
25
+ "@webstudio-is/css-engine": "0.151.0",
26
+ "@webstudio-is/fonts": "0.151.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@jest/globals": "^29.7.0",