@webstudio-is/sdk 0.168.0 → 0.173.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
@@ -322,6 +322,12 @@ var Prop = z6.union([
322
322
  // data source id
323
323
  value: z6.string()
324
324
  }),
325
+ z6.object({
326
+ ...baseProp,
327
+ type: z6.literal("resource"),
328
+ // resource id
329
+ value: z6.string()
330
+ }),
325
331
  z6.object({
326
332
  ...baseProp,
327
333
  type: z6.literal("expression"),
@@ -827,7 +833,7 @@ var getStaticSiteMapXml = (pages, updatedAt) => {
827
833
  };
828
834
 
829
835
  // src/scope.ts
830
- var normalizeName = (name) => {
836
+ var normalizeJsName = (name) => {
831
837
  name = name.replaceAll(/[^\w$]/g, "");
832
838
  if (name.length === 0) {
833
839
  return "_";
@@ -837,7 +843,7 @@ var normalizeName = (name) => {
837
843
  }
838
844
  return name;
839
845
  };
840
- var createScope = (occupiedIdentifiers = []) => {
846
+ var createScope = (occupiedIdentifiers = [], normalizeName = normalizeJsName, separator = "_") => {
841
847
  const freeIndexByPreferredName = /* @__PURE__ */ new Map();
842
848
  const scopedNameByIdMap = /* @__PURE__ */ new Map();
843
849
  for (const identifier of occupiedIdentifiers) {
@@ -853,7 +859,7 @@ var createScope = (occupiedIdentifiers = []) => {
853
859
  freeIndexByPreferredName.set(preferredName, (index ?? 0) + 1);
854
860
  let scopedName = preferredName;
855
861
  if (index !== void 0) {
856
- scopedName = `${preferredName}_${index}`;
862
+ scopedName = `${preferredName}${separator}${index}`;
857
863
  }
858
864
  scopedNameByIdMap.set(id, scopedName);
859
865
  return scopedName;
@@ -864,8 +870,8 @@ var createScope = (occupiedIdentifiers = []) => {
864
870
  };
865
871
 
866
872
  // src/resource-loader.ts
867
- var loadResource = async (customFetch, resourceData) => {
868
- const { url, method, headers, body } = resourceData;
873
+ var loadResource = async (customFetch, resourceRequest) => {
874
+ const { url, method, headers, body } = resourceRequest;
869
875
  const requestHeaders = new Headers(
870
876
  headers.map(({ name, value }) => [name, value])
871
877
  );
@@ -885,12 +891,13 @@ var loadResource = async (customFetch, resourceData) => {
885
891
  const response = await customFetch(url.trim(), requestInit);
886
892
  let data;
887
893
  if (response.ok && // accept json by default and when specified explicitly
888
- (requestHeaders.has("accept") === false || requestHeaders.get("accept") === "application/json")) {
894
+ (response.headers.has("content-type") === false || response.headers.get("content-type")?.includes("application/json"))) {
889
895
  data = await response.json();
890
896
  } else {
891
897
  data = await response.text();
892
898
  }
893
899
  return {
900
+ ok: response.ok,
894
901
  data,
895
902
  status: response.status,
896
903
  statusText: response.statusText
@@ -898,108 +905,87 @@ var loadResource = async (customFetch, resourceData) => {
898
905
  } catch (error) {
899
906
  const message = error.message;
900
907
  return {
908
+ ok: false,
901
909
  data: void 0,
902
910
  status: 500,
903
911
  statusText: message
904
912
  };
905
913
  }
906
914
  };
907
-
908
- // src/forms-generator.ts
909
- var generateFormsProperties = (props) => {
910
- const formsProperties = /* @__PURE__ */ new Map();
911
- for (const prop of props.values()) {
912
- if (prop.type === "string") {
913
- if (prop.name === "action" || prop.name === "method") {
914
- let properties = formsProperties.get(prop.instanceId);
915
- if (properties === void 0) {
916
- properties = {};
917
- }
918
- properties[prop.name] = prop.value;
919
- formsProperties.set(prop.instanceId, properties);
920
- }
921
- }
922
- }
923
- const entriesString = JSON.stringify(Array.from(formsProperties.entries()));
924
- let generated = "";
925
- generated += `type FormProperties = { method?: string, action?: string }
926
- `;
927
- generated += `export const formsProperties = new Map<string, FormProperties>(${entriesString})
928
- `;
929
- return generated;
915
+ var loadResources = async (customFetch, requests) => {
916
+ return Object.fromEntries(
917
+ await Promise.all(
918
+ Array.from(
919
+ requests,
920
+ async ([name, request]) => [name, await loadResource(customFetch, request)]
921
+ )
922
+ )
923
+ );
930
924
  };
931
925
 
932
926
  // src/resources-generator.ts
933
- var generateResourcesLoader = ({
927
+ var generateResources = ({
934
928
  scope,
935
929
  page,
936
930
  dataSources,
931
+ props,
937
932
  resources
938
933
  }) => {
939
- let generatedOutput = "";
940
- let generatedLoaders = "";
941
- let hasResources = false;
942
934
  const usedDataSources = /* @__PURE__ */ new Map();
943
- for (const dataSource of dataSources.values()) {
944
- if (dataSource.type === "resource") {
945
- const resource = resources.get(dataSource.resourceId);
946
- if (resource === void 0) {
947
- continue;
948
- }
949
- hasResources = true;
950
- const resourceName = scope.getName(resource.id, dataSource.name);
951
- generatedOutput += `${resourceName},
935
+ let generatedRequests = "";
936
+ for (const resource of resources.values()) {
937
+ let generatedRequest = "";
938
+ const resourceName = scope.getName(resource.id, resource.name);
939
+ generatedRequest += ` const ${resourceName}: ResourceRequest = {
952
940
  `;
953
- generatedLoaders += `loadResource(customFetch, {
941
+ generatedRequest += ` id: "${resource.id}",
954
942
  `;
955
- generatedLoaders += `id: "${resource.id}",
943
+ generatedRequest += ` name: ${JSON.stringify(resource.name)},
956
944
  `;
957
- generatedLoaders += `name: ${JSON.stringify(resource.name)},
945
+ const url = generateExpression({
946
+ expression: resource.url,
947
+ dataSources,
948
+ usedDataSources,
949
+ scope
950
+ });
951
+ generatedRequest += ` url: ${url},
958
952
  `;
959
- const url = generateExpression({
960
- expression: resource.url,
953
+ generatedRequest += ` method: "${resource.method}",
954
+ `;
955
+ generatedRequest += ` headers: [
956
+ `;
957
+ for (const header of resource.headers) {
958
+ const value = generateExpression({
959
+ expression: header.value,
961
960
  dataSources,
962
961
  usedDataSources,
963
962
  scope
964
963
  });
965
- generatedLoaders += `url: ${url},
966
- `;
967
- generatedLoaders += `method: "${resource.method}",
968
- `;
969
- generatedLoaders += `headers: [
964
+ generatedRequest += ` { name: "${header.name}", value: ${value} },
970
965
  `;
971
- for (const header of resource.headers) {
972
- const value = generateExpression({
973
- expression: header.value,
974
- dataSources,
975
- usedDataSources,
976
- scope
977
- });
978
- generatedLoaders += `{ name: "${header.name}", value: ${value} },
979
- `;
980
- }
981
- generatedLoaders += `],
982
- `;
983
- if (resource.body !== void 0 && resource.body.length > 0) {
984
- const body = generateExpression({
985
- expression: resource.body,
986
- dataSources,
987
- usedDataSources,
988
- scope
989
- });
990
- generatedLoaders += `body: ${body},
966
+ }
967
+ generatedRequest += ` ],
991
968
  `;
992
- }
993
- generatedLoaders += `}),
969
+ if (resource.body !== void 0 && resource.body.length > 0) {
970
+ const body = generateExpression({
971
+ expression: resource.body,
972
+ dataSources,
973
+ usedDataSources,
974
+ scope
975
+ });
976
+ generatedRequest += ` body: ${body},
994
977
  `;
995
978
  }
979
+ generatedRequest += ` }
980
+ `;
981
+ generatedRequests += generatedRequest;
996
982
  }
997
983
  let generatedVariables = "";
998
984
  for (const dataSource of usedDataSources.values()) {
999
985
  if (dataSource.type === "variable") {
1000
986
  const name = scope.getName(dataSource.id, dataSource.name);
1001
987
  const value = JSON.stringify(dataSource.value.value);
1002
- generatedVariables += `let ${name} = ${value}
988
+ generatedVariables += ` let ${name} = ${value}
1003
989
  `;
1004
990
  }
1005
991
  if (dataSource.type === "parameter") {
@@ -1007,61 +993,108 @@ var generateResourcesLoader = ({
1007
993
  continue;
1008
994
  }
1009
995
  const name = scope.getName(dataSource.id, dataSource.name);
1010
- generatedVariables += `const ${name} = _props.system
996
+ generatedVariables += ` const ${name} = _props.system
1011
997
  `;
1012
998
  }
1013
999
  }
1014
1000
  let generated = "";
1015
- generated += `import { loadResource, isLocalResource, type System } from "@webstudio-is/sdk";
1001
+ generated += `import type { System, ResourceRequest } from "@webstudio-is/sdk";
1016
1002
  `;
1017
- if (hasResources) {
1018
- generated += `import { sitemap } from "./$resources.sitemap.xml";
1019
- `;
1020
- }
1021
- generated += `export const loadResources = async (_props: { system: System }) => {
1003
+ generated += `export const getResources = (_props: { system: System }) => {
1022
1004
  `;
1023
1005
  generated += generatedVariables;
1024
- if (hasResources) {
1025
- generated += `
1026
- const customFetch: typeof fetch = (input, init) => {
1027
- if (typeof input !== "string") {
1028
- return fetch(input, init);
1029
- }
1030
-
1031
- if (isLocalResource(input, "sitemap.xml")) {
1032
- // @todo: dynamic import sitemap ???
1033
- const response = new Response(JSON.stringify(sitemap));
1034
- response.headers.set('content-type', 'application/json; charset=utf-8');
1035
- return Promise.resolve(response);
1036
- }
1037
-
1038
- return fetch(input, init);
1039
- };
1040
- `;
1041
- generated += `const [
1006
+ generated += generatedRequests;
1007
+ generated += ` const _data = new Map<string, ResourceRequest>([
1042
1008
  `;
1043
- generated += generatedOutput;
1044
- generated += `] = await Promise.all([
1009
+ for (const dataSource of dataSources.values()) {
1010
+ if (dataSource.type === "resource") {
1011
+ const name = scope.getName(dataSource.resourceId, dataSource.name);
1012
+ generated += ` ["${name}", ${name}],
1045
1013
  `;
1046
- generated += generatedLoaders;
1047
- generated += `])
1014
+ }
1015
+ }
1016
+ generated += ` ])
1017
+ `;
1018
+ generated += ` const _action = new Map<string, ResourceRequest>([
1019
+ `;
1020
+ for (const prop of props.values()) {
1021
+ if (prop.type === "resource") {
1022
+ const name = scope.getName(prop.value, prop.name);
1023
+ generated += ` ["${name}", ${name}],
1048
1024
  `;
1025
+ }
1049
1026
  }
1050
- generated += `return {
1027
+ generated += ` ])
1051
1028
  `;
1052
- generated += generatedOutput;
1053
- generated += `} as Record<string, unknown>
1029
+ generated += ` return { data: _data, action: _action }
1054
1030
  `;
1055
1031
  generated += `}
1056
1032
  `;
1057
1033
  return generated;
1058
1034
  };
1035
+ var getMethod = (value) => {
1036
+ switch (value?.toLowerCase()) {
1037
+ case "get":
1038
+ return "get";
1039
+ case "delete":
1040
+ return "delete";
1041
+ case "put":
1042
+ return "put";
1043
+ default:
1044
+ return "post";
1045
+ }
1046
+ };
1047
+ var replaceFormActionsWithResources = ({
1048
+ props,
1049
+ instances,
1050
+ resources
1051
+ }) => {
1052
+ const formProps = /* @__PURE__ */ new Map();
1053
+ for (const prop of props.values()) {
1054
+ if (prop.name === "method" && prop.type === "string" && instances.get(prop.instanceId)?.component === "Form") {
1055
+ let data = formProps.get(prop.instanceId);
1056
+ if (data === void 0) {
1057
+ data = {};
1058
+ formProps.set(prop.instanceId, data);
1059
+ }
1060
+ data.method = prop.value;
1061
+ props.delete(prop.id);
1062
+ }
1063
+ if (prop.name === "action" && prop.type === "string" && instances.get(prop.instanceId)?.component === "Form") {
1064
+ let data = formProps.get(prop.instanceId);
1065
+ if (data === void 0) {
1066
+ data = {};
1067
+ formProps.set(prop.instanceId, data);
1068
+ }
1069
+ data.action = prop.value;
1070
+ props.set(prop.id, {
1071
+ id: prop.id,
1072
+ instanceId: prop.instanceId,
1073
+ name: prop.name,
1074
+ type: "resource",
1075
+ value: prop.instanceId
1076
+ });
1077
+ }
1078
+ }
1079
+ for (const [instanceId, { action, method }] of formProps) {
1080
+ if (action) {
1081
+ resources.set(instanceId, {
1082
+ id: instanceId,
1083
+ name: "action",
1084
+ method: getMethod(method),
1085
+ url: JSON.stringify(action),
1086
+ headers: []
1087
+ });
1088
+ }
1089
+ }
1090
+ };
1059
1091
 
1060
1092
  // src/page-meta-generator.ts
1061
1093
  var generatePageMeta = ({
1062
1094
  globalScope,
1063
1095
  page,
1064
- dataSources
1096
+ dataSources,
1097
+ assets
1065
1098
  }) => {
1066
1099
  const localScope = createScope(["system", "resources"]);
1067
1100
  const usedDataSources = /* @__PURE__ */ new Map();
@@ -1089,8 +1122,8 @@ var generatePageMeta = ({
1089
1122
  usedDataSources,
1090
1123
  scope: localScope
1091
1124
  });
1092
- const socialImageAssetIdExpression = JSON.stringify(
1093
- page.meta.socialImageAssetId
1125
+ const socialImageAssetNameExpression = JSON.stringify(
1126
+ page.meta.socialImageAssetId ? assets.get(page.meta.socialImageAssetId)?.name : void 0
1094
1127
  );
1095
1128
  const socialImageUrlExpression = generateExpression({
1096
1129
  expression: page.meta.socialImageUrl ?? "undefined",
@@ -1186,7 +1219,7 @@ var generatePageMeta = ({
1186
1219
  `;
1187
1220
  generated += ` language: ${languageExpression},
1188
1221
  `;
1189
- generated += ` socialImageAssetId: ${socialImageAssetIdExpression},
1222
+ generated += ` socialImageAssetName: ${socialImageAssetNameExpression},
1190
1223
  `;
1191
1224
  generated += ` socialImageUrl: ${socialImageUrlExpression},
1192
1225
  `;
@@ -1253,10 +1286,9 @@ export {
1253
1286
  findTreeInstanceIds,
1254
1287
  findTreeInstanceIdsExcludingSlotDescendants,
1255
1288
  generateExpression,
1256
- generateFormsProperties,
1257
1289
  generateObjectExpression,
1258
1290
  generatePageMeta,
1259
- generateResourcesLoader,
1291
+ generateResources,
1260
1292
  getExpressionIdentifiers,
1261
1293
  getPagePath,
1262
1294
  getStaticSiteMapXml,
@@ -1268,9 +1300,11 @@ export {
1268
1300
  isRoot,
1269
1301
  lintExpression,
1270
1302
  loadResource,
1303
+ loadResources,
1271
1304
  matchPathnameParams,
1272
1305
  parseComponentName,
1273
1306
  parseObjectExpression,
1307
+ replaceFormActionsWithResources,
1274
1308
  sitemapResourceUrl,
1275
1309
  transpileExpression
1276
1310
  };
@@ -15,7 +15,6 @@ export * from "./page-utils";
15
15
  export * from "./scope";
16
16
  export * from "./resource-loader";
17
17
  export * from "./expression";
18
- export * from "./forms-generator";
19
18
  export * from "./resources-generator";
20
19
  export * from "./page-meta-generator";
21
20
  export * from "./url-pattern";
@@ -1,36 +1,4 @@
1
- import type { Instance } from "./schema/instances";
2
- export declare const findTreeInstanceIds: (instances: Map<string, {
3
- type: "instance";
4
- id: string;
5
- children: ({
6
- value: string;
7
- type: "text";
8
- placeholder?: boolean | undefined;
9
- } | {
10
- value: string;
11
- type: "id";
12
- } | {
13
- value: string;
14
- type: "expression";
15
- })[];
16
- component: string;
17
- label?: string | undefined;
18
- }>, rootInstanceId: Instance["id"]) => Set<string>;
19
- export declare const findTreeInstanceIdsExcludingSlotDescendants: (instances: Map<string, {
20
- type: "instance";
21
- id: string;
22
- children: ({
23
- value: string;
24
- type: "text";
25
- placeholder?: boolean | undefined;
26
- } | {
27
- value: string;
28
- type: "id";
29
- } | {
30
- value: string;
31
- type: "expression";
32
- })[];
33
- component: string;
34
- label?: string | undefined;
35
- }>, rootInstanceId: Instance["id"]) => Set<string>;
1
+ import type { Instance, Instances } from "./schema/instances";
2
+ export declare const findTreeInstanceIds: (instances: Instances, rootInstanceId: Instance["id"]) => Set<string>;
3
+ export declare const findTreeInstanceIdsExcludingSlotDescendants: (instances: Instances, rootInstanceId: Instance["id"]) => Set<string>;
36
4
  export declare const parseComponentName: (componentName: string) => readonly [string | undefined, string];
@@ -0,0 +1,151 @@
1
+ import type { ReactNode } from "react";
2
+ export declare class ExpressionValue {
3
+ value: string;
4
+ constructor(expression: string);
5
+ }
6
+ export declare class ParameterValue {
7
+ value: string;
8
+ constructor(dataSourceID: string);
9
+ }
10
+ export declare class ResourceValue {
11
+ value: string;
12
+ constructor(resourceId: string);
13
+ }
14
+ export declare class ActionValue {
15
+ value: {
16
+ type: "execute";
17
+ args: string[];
18
+ code: string;
19
+ };
20
+ constructor(args: string[], code: string);
21
+ }
22
+ export declare class AssetValue {
23
+ value: string;
24
+ constructor(assetId: string);
25
+ }
26
+ export declare class PageValue {
27
+ value: string | {
28
+ pageId: string;
29
+ instanceId: string;
30
+ };
31
+ constructor(pageId: string, instanceId?: string);
32
+ }
33
+ export declare const renderJsx: (root: JSX.Element) => {
34
+ instances: Map<string, {
35
+ type: "instance";
36
+ id: string;
37
+ children: ({
38
+ value: string;
39
+ type: "text";
40
+ placeholder?: boolean | undefined;
41
+ } | {
42
+ value: string;
43
+ type: "id";
44
+ } | {
45
+ value: string;
46
+ type: "expression";
47
+ })[];
48
+ component: string;
49
+ label?: string | undefined;
50
+ }>;
51
+ props: Map<string, {
52
+ value: number;
53
+ type: "number";
54
+ id: string;
55
+ name: string;
56
+ instanceId: string;
57
+ required?: boolean | undefined;
58
+ } | {
59
+ value: string;
60
+ type: "string";
61
+ id: string;
62
+ name: string;
63
+ instanceId: string;
64
+ required?: boolean | undefined;
65
+ } | {
66
+ value: boolean;
67
+ type: "boolean";
68
+ id: string;
69
+ name: string;
70
+ instanceId: string;
71
+ required?: boolean | undefined;
72
+ } | {
73
+ type: "json";
74
+ id: string;
75
+ name: string;
76
+ instanceId: string;
77
+ value?: unknown;
78
+ required?: boolean | undefined;
79
+ } | {
80
+ value: string;
81
+ type: "asset";
82
+ id: string;
83
+ name: string;
84
+ instanceId: string;
85
+ required?: boolean | undefined;
86
+ } | {
87
+ value: (string | {
88
+ instanceId: string;
89
+ pageId: string;
90
+ }) & (string | {
91
+ instanceId: string;
92
+ pageId: string;
93
+ } | undefined);
94
+ type: "page";
95
+ id: string;
96
+ name: string;
97
+ instanceId: string;
98
+ required?: boolean | undefined;
99
+ } | {
100
+ value: string[];
101
+ type: "string[]";
102
+ id: string;
103
+ name: string;
104
+ instanceId: string;
105
+ required?: boolean | undefined;
106
+ } | {
107
+ value: string;
108
+ type: "parameter";
109
+ id: string;
110
+ name: string;
111
+ instanceId: string;
112
+ required?: boolean | undefined;
113
+ } | {
114
+ value: string;
115
+ type: "resource";
116
+ id: string;
117
+ name: string;
118
+ instanceId: string;
119
+ required?: boolean | undefined;
120
+ } | {
121
+ value: string;
122
+ type: "expression";
123
+ id: string;
124
+ name: string;
125
+ instanceId: string;
126
+ required?: boolean | undefined;
127
+ } | {
128
+ value: {
129
+ code: string;
130
+ type: "execute";
131
+ args: string[];
132
+ }[];
133
+ type: "action";
134
+ id: string;
135
+ name: string;
136
+ instanceId: string;
137
+ required?: boolean | undefined;
138
+ }>;
139
+ };
140
+ type ComponentProps = Record<string, unknown> & Record<`${string}:expression`, string> & {
141
+ "ws:id"?: string;
142
+ "ws:label"?: string;
143
+ children?: ReactNode;
144
+ };
145
+ type Component = {
146
+ displayName: string;
147
+ } & ((props: ComponentProps) => ReactNode);
148
+ export declare const createProxy: (prefix: string) => Record<string, Component>;
149
+ export declare const $: Record<string, Component>;
150
+ export declare const ws: Record<string, Component>;
151
+ export {};
@@ -1,4 +1,4 @@
1
- import type { Asset } from "./schema/assets";
1
+ import type { Asset, Assets } from "./schema/assets";
2
2
  import type { DataSources } from "./schema/data-sources";
3
3
  import type { Page } from "./schema/pages";
4
4
  import { type Scope } from "./scope";
@@ -7,7 +7,7 @@ export type PageMeta = {
7
7
  description?: string;
8
8
  excludePageFromSearch?: boolean;
9
9
  language?: string;
10
- socialImageAssetId?: Asset["id"];
10
+ socialImageAssetName?: Asset["name"];
11
11
  socialImageUrl?: string;
12
12
  status?: number;
13
13
  redirect?: string;
@@ -16,8 +16,9 @@ export type PageMeta = {
16
16
  content: string;
17
17
  }>;
18
18
  };
19
- export declare const generatePageMeta: ({ globalScope, page, dataSources, }: {
19
+ export declare const generatePageMeta: ({ globalScope, page, dataSources, assets, }: {
20
20
  globalScope: Scope;
21
21
  page: Page;
22
22
  dataSources: DataSources;
23
+ assets: Assets;
23
24
  }) => string;
@@ -1,6 +1,15 @@
1
1
  import type { ResourceRequest } from "./schema/resources";
2
- export declare const loadResource: (customFetch: typeof fetch, resourceData: ResourceRequest) => Promise<{
2
+ export declare const loadResource: (customFetch: typeof fetch, resourceRequest: ResourceRequest) => Promise<{
3
+ ok: boolean;
3
4
  data: any;
4
5
  status: number;
5
6
  statusText: string;
6
7
  }>;
8
+ export declare const loadResources: (customFetch: typeof fetch, requests: Map<string, ResourceRequest>) => Promise<{
9
+ [k: string]: {
10
+ ok: boolean;
11
+ data: any;
12
+ status: number;
13
+ statusText: string;
14
+ };
15
+ }>;
@@ -1,10 +1,22 @@
1
1
  import type { DataSources } from "./schema/data-sources";
2
2
  import type { Page } from "./schema/pages";
3
3
  import type { Resources } from "./schema/resources";
4
+ import type { Props } from "./schema/props";
5
+ import type { Instances } from "./schema/instances";
4
6
  import type { Scope } from "./scope";
5
- export declare const generateResourcesLoader: ({ scope, page, dataSources, resources, }: {
7
+ export declare const generateResources: ({ scope, page, dataSources, props, resources, }: {
6
8
  scope: Scope;
7
9
  page: Page;
8
10
  dataSources: DataSources;
11
+ props: Props;
9
12
  resources: Resources;
10
13
  }) => string;
14
+ /**
15
+ * migrate webhook forms to resource action
16
+ * @todo move to client migrations eventually
17
+ */
18
+ export declare const replaceFormActionsWithResources: ({ props, instances, resources, }: {
19
+ props: Props;
20
+ instances: Instances;
21
+ resources: Resources;
22
+ }) => void;