@xlr-lib/xlr-sdk 0.1.1--canary.9.190

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.
@@ -0,0 +1,303 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import type { NamedType, OrType, ObjectType } from "@xlr-lib/xlr";
3
+ import { parseTree } from "jsonc-parser";
4
+ import { Types, ReferenceAssetsWebPluginManifest } from "@xlr-lib/static-xlrs";
5
+ import type { Filters } from "../registry";
6
+ import { XLRSDK } from "../sdk";
7
+ import { XLRValidator } from "../validator";
8
+ import { ValidationMessage } from "../types";
9
+
10
+ const EXCLUDE: Filters = { typeFilter: "Transformed" };
11
+
12
+ describe("Loading XLRs", () => {
13
+ test("Loading from Disk", () => {
14
+ const sdk = new XLRSDK();
15
+ sdk.loadDefinitionsFromModule(Types);
16
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
17
+
18
+ expect(sdk.hasType("Asset")).toStrictEqual(true);
19
+ expect(sdk.hasType("AssetWrapper")).toStrictEqual(true);
20
+ expect(sdk.hasType("Binding")).toStrictEqual(true);
21
+ expect(sdk.hasType("BindingRef")).toStrictEqual(true);
22
+ expect(sdk.hasType("ExpressionRef")).toStrictEqual(true);
23
+ expect(sdk.hasType("ActionAsset")).toStrictEqual(true);
24
+ expect(sdk.hasType("InputAsset")).toStrictEqual(true);
25
+ expect(sdk.hasType("TransformedAction")).toStrictEqual(false);
26
+ expect(sdk.hasType("TransformedInput")).toStrictEqual(false);
27
+ });
28
+
29
+ test("Loading from Module", async () => {
30
+ const sdk = new XLRSDK();
31
+ await sdk.loadDefinitionsFromModule(
32
+ ReferenceAssetsWebPluginManifest,
33
+ EXCLUDE,
34
+ );
35
+ await sdk.loadDefinitionsFromModule(Types);
36
+
37
+ expect(sdk.hasType("Asset")).toStrictEqual(true);
38
+ expect(sdk.hasType("AssetWrapper")).toStrictEqual(true);
39
+ expect(sdk.hasType("Binding")).toStrictEqual(true);
40
+ expect(sdk.hasType("BindingRef")).toStrictEqual(true);
41
+ expect(sdk.hasType("ExpressionRef")).toStrictEqual(true);
42
+ expect(sdk.hasType("ActionAsset")).toStrictEqual(true);
43
+ expect(sdk.hasType("InputAsset")).toStrictEqual(true);
44
+ expect(sdk.hasType("TransformedAction")).toStrictEqual(false);
45
+ expect(sdk.hasType("TransformedInput")).toStrictEqual(false);
46
+ });
47
+ });
48
+
49
+ describe("Object Recall", () => {
50
+ test("Raw", () => {
51
+ const sdk = new XLRSDK();
52
+ sdk.loadDefinitionsFromModule(Types);
53
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
54
+
55
+ expect(sdk.getType("InputAsset", { getRawType: true })).toMatchSnapshot();
56
+ });
57
+
58
+ test("Processed", () => {
59
+ const sdk = new XLRSDK();
60
+ sdk.loadDefinitionsFromModule(Types);
61
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
62
+
63
+ expect(sdk.getType("InputAsset", { optimize: false })).toMatchSnapshot();
64
+ });
65
+
66
+ test("Optimized", () => {
67
+ const sdk = new XLRSDK();
68
+ sdk.loadDefinitionsFromModule(Types);
69
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
70
+
71
+ expect(sdk.getType("InputAsset")).toMatchSnapshot();
72
+ });
73
+
74
+ test("Test Correct Generic Cascading", () => {
75
+ const sdk = new XLRSDK();
76
+ sdk.loadDefinitionsFromModule(Types);
77
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
78
+ const Flow = sdk.getType("Flow") as ObjectType;
79
+ const Schema = Flow.properties["schema"].node as ObjectType;
80
+ const Node = Schema.additionalProperties as ObjectType;
81
+ const DataTypes = Node.additionalProperties as OrType;
82
+ const DataType = DataTypes.or[0] as ObjectType;
83
+ const df = DataType.properties["default"].node;
84
+ expect(df.type).toBe("unknown");
85
+ });
86
+ });
87
+
88
+ describe("Validation", () => {
89
+ test("Basic Validation By Name", () => {
90
+ const mockAsset = parseTree(`
91
+ {
92
+ "id": 1,
93
+ "type": "input",
94
+ "binding": "some.data",
95
+ "label": {
96
+ "asset": {
97
+ "value": "{{input.label}}"
98
+ }
99
+ }
100
+ `);
101
+
102
+ const sdk = new XLRSDK();
103
+ sdk.loadDefinitionsFromModule(Types);
104
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
105
+
106
+ expect(sdk.validateByName("InputAsset", mockAsset)).toMatchSnapshot();
107
+ });
108
+
109
+ test("Basic Validation By Type (unoptimized)", () => {
110
+ const mockAsset = parseTree(`
111
+ {
112
+ "id": 1,
113
+ "type": "input",
114
+ "binding": "some.data",
115
+ "label": {
116
+ "asset": {
117
+ "value": "{{input.label}}"
118
+ }
119
+ }
120
+ `);
121
+
122
+ const sdk = new XLRSDK();
123
+ sdk.loadDefinitionsFromModule(Types);
124
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
125
+
126
+ const inputAsset = sdk.getType("InputAsset", { optimize: false });
127
+ expect(inputAsset).toBeDefined();
128
+ expect(
129
+ sdk.validateByType(inputAsset as NamedType, mockAsset),
130
+ ).toMatchSnapshot();
131
+ });
132
+
133
+ test("Basic Validation By Type (optimized)", () => {
134
+ const mockAsset = parseTree(`
135
+ {
136
+ "id": 1,
137
+ "type": "input",
138
+ "binding": "some.data",
139
+ "label": {
140
+ "asset": {
141
+ "value": "{{input.label}}"
142
+ }
143
+ }
144
+ `);
145
+
146
+ const sdk = new XLRSDK();
147
+ sdk.loadDefinitionsFromModule(Types);
148
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
149
+
150
+ const inputAsset = sdk.getType("InputAsset");
151
+ expect(inputAsset).toBeDefined();
152
+ expect(
153
+ sdk.validateByType(inputAsset as NamedType, mockAsset),
154
+ ).toMatchSnapshot();
155
+ });
156
+ });
157
+
158
+ describe("Or Type Validation", () => {
159
+ test("Outputs helpful type error messages", () => {
160
+ const mockAsset = parseTree(`
161
+ {
162
+ "type": "string",
163
+ "value": "orange"
164
+ }`);
165
+
166
+ const orType: OrType = {
167
+ type: "or",
168
+ or: [
169
+ { type: "string", const: "apple" },
170
+ { type: "string", const: "banana" },
171
+ { type: "string", const: "carrot" },
172
+ { type: "string", const: "deli-meat" },
173
+ ],
174
+ };
175
+
176
+ const sdk = new XLRSDK();
177
+ sdk.loadDefinitionsFromModule(Types);
178
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
179
+
180
+ const validator = new XLRValidator(sdk.getType);
181
+ let validationResult: ValidationMessage[];
182
+
183
+ if (mockAsset.children && mockAsset.children.length > 0) {
184
+ validationResult = validator.validateType(mockAsset.children[0], orType);
185
+ } else {
186
+ validationResult = [];
187
+ }
188
+
189
+ // Expected error and info messages
190
+ expect(validationResult).toHaveLength(2);
191
+ expect(validationResult[0].message).toBe(
192
+ "Does not match any of the types: string | string | string | string",
193
+ );
194
+ expect(validationResult[1].message).toBe(
195
+ "Expected: apple | banana | carrot | deli-meat",
196
+ );
197
+ });
198
+ });
199
+
200
+ describe("generateNestedTypesInfo Test", () => {
201
+ test("Test with potentialTypeErrors", () => {
202
+ const sdk = new XLRSDK();
203
+ sdk.loadDefinitionsFromModule(Types);
204
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
205
+ const validator = new XLRValidator(sdk.getType);
206
+
207
+ const potentialTypeErrors = [
208
+ {
209
+ type: { type: "string" },
210
+ errors: [
211
+ {
212
+ type: "type",
213
+ node: {} as any,
214
+ message: "Expected string",
215
+ severity: 1,
216
+ expected: "string",
217
+ },
218
+ ],
219
+ },
220
+ ];
221
+
222
+ const xlrNode = {
223
+ type: "or",
224
+ or: [{ type: "string" }, { type: "number" }],
225
+ } as OrType;
226
+
227
+ const rootNode = {
228
+ type: "boolean",
229
+ value: true,
230
+ } as any;
231
+
232
+ const result = (validator as any).generateNestedTypesInfo(
233
+ potentialTypeErrors,
234
+ xlrNode,
235
+ rootNode,
236
+ );
237
+
238
+ expect(result.nestedTypesList).toBe("string");
239
+ expect(result.infoMessage).toBe("Got: true and expected: string");
240
+ });
241
+
242
+ test("Works with urlMapping", () => {
243
+ const sdk = new XLRSDK();
244
+ sdk.loadDefinitionsFromModule(Types);
245
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
246
+ const validator = new XLRValidator(sdk.getType, {
247
+ urlMapping: {
248
+ MyType: "https://example.com/mytype",
249
+ },
250
+ });
251
+
252
+ const potentialTypeErrors: any[] = [];
253
+
254
+ const xlrNode = {
255
+ type: "or",
256
+ name: "MyType",
257
+ or: [{ type: "string" }, { type: "number" }],
258
+ } as OrType;
259
+
260
+ const rootNode = {
261
+ type: "boolean",
262
+ value: true,
263
+ } as any;
264
+
265
+ const result = (validator as any).generateNestedTypesInfo(
266
+ potentialTypeErrors,
267
+ xlrNode,
268
+ rootNode,
269
+ );
270
+
271
+ expect(result.nestedTypesList).toBe("https://example.com/mytype");
272
+ expect(result.infoMessage).toBe(
273
+ "Got: true and expected: https://example.com/mytype",
274
+ );
275
+ });
276
+
277
+ test("Without rootNode value", () => {
278
+ const sdk = new XLRSDK();
279
+ sdk.loadDefinitionsFromModule(Types);
280
+ sdk.loadDefinitionsFromModule(ReferenceAssetsWebPluginManifest);
281
+ const validator = new XLRValidator(sdk.getType);
282
+
283
+ const potentialTypeErrors: any[] = [];
284
+
285
+ const xlrNode = {
286
+ type: "or",
287
+ or: [{ type: "string" }, { type: "number" }],
288
+ } as OrType;
289
+
290
+ const rootNode = {
291
+ type: "boolean",
292
+ } as any;
293
+
294
+ const result = (validator as any).generateNestedTypesInfo(
295
+ potentialTypeErrors,
296
+ xlrNode,
297
+ rootNode,
298
+ );
299
+
300
+ expect(result.nestedTypesList).toBe("string | number");
301
+ expect(result.infoMessage).toBe("Expected: string | number");
302
+ });
303
+ });
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./sdk";
2
+ export * from "./types";
3
+ export * from "./registry";
4
+ export * from "./utils";
@@ -0,0 +1,82 @@
1
+ import type { NamedType, NodeType } from "@xlr-lib/xlr";
2
+ import type { XLRRegistry, Filters, TypeMetadata } from "./types";
3
+
4
+ /**
5
+ * Basic example of a XLRs Registry
6
+ */
7
+ export class BasicXLRRegistry implements XLRRegistry {
8
+ private typeMap: Map<string, NamedType<NodeType>>;
9
+ private pluginMap: Map<string, Map<string, Array<string>>>;
10
+ private infoMap: Map<string, TypeMetadata>;
11
+
12
+ constructor() {
13
+ this.typeMap = new Map();
14
+ this.pluginMap = new Map();
15
+ this.infoMap = new Map();
16
+ }
17
+
18
+ /** Returns a copy of the XLR to guard against unexpected type modification */
19
+ get(id: string): NamedType<NodeType> | undefined {
20
+ const value = this.typeMap.get(id);
21
+ return value ? JSON.parse(JSON.stringify(value)) : undefined;
22
+ }
23
+
24
+ add(type: NamedType<NodeType>, plugin: string, capability: string): void {
25
+ this.typeMap.set(type.name, type);
26
+ this.infoMap.set(type.name, { plugin, capability });
27
+
28
+ if (!this.pluginMap.has(plugin)) {
29
+ this.pluginMap.set(plugin, new Map());
30
+ }
31
+
32
+ const pluginsCapabilities = this.pluginMap.get(plugin) as Map<
33
+ string,
34
+ Array<string>
35
+ >;
36
+
37
+ if (!pluginsCapabilities.has(capability)) {
38
+ pluginsCapabilities.set(capability, []);
39
+ }
40
+
41
+ const providedCapabilities = pluginsCapabilities.get(
42
+ capability,
43
+ ) as string[];
44
+ providedCapabilities.push(type.name);
45
+ }
46
+
47
+ has(id: string): boolean {
48
+ return this.typeMap.has(id);
49
+ }
50
+
51
+ list(filterArgs?: Filters): NamedType<NodeType>[] {
52
+ const validTypes: Array<string> = [];
53
+
54
+ this.pluginMap.forEach((manifest, pluginName) => {
55
+ if (
56
+ !filterArgs?.pluginFilter ||
57
+ !pluginName.match(filterArgs.pluginFilter)
58
+ ) {
59
+ manifest.forEach((types, capabilityName) => {
60
+ if (
61
+ !filterArgs?.capabilityFilter ||
62
+ !capabilityName.match(filterArgs.capabilityFilter)
63
+ ) {
64
+ types.forEach((type) => {
65
+ if (
66
+ !filterArgs?.typeFilter ||
67
+ !type.match(filterArgs.typeFilter)
68
+ ) {
69
+ validTypes.push(type);
70
+ }
71
+ });
72
+ }
73
+ });
74
+ }
75
+ });
76
+ return validTypes.map((type) => this.get(type) as NamedType);
77
+ }
78
+
79
+ info(id: string): TypeMetadata | undefined {
80
+ return this.infoMap.get(id);
81
+ }
82
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./basic-registry";
2
+ export * from "./types";
@@ -0,0 +1,28 @@
1
+ import type { NamedType } from "@xlr-lib/xlr";
2
+
3
+ export interface Filters {
4
+ /** filter based on plugin name */
5
+ pluginFilter?: string | RegExp;
6
+
7
+ /** filter based on capability name */
8
+ capabilityFilter?: string | RegExp;
9
+
10
+ /** filter based on type name */
11
+ typeFilter?: string | RegExp;
12
+ }
13
+
14
+ export interface TypeMetadata {
15
+ /** The Plugin the Type comes from */
16
+ plugin: string;
17
+
18
+ /** The Capability of the Type */
19
+ capability: string;
20
+ }
21
+
22
+ export interface XLRRegistry {
23
+ get(id: string): NamedType | undefined;
24
+ add(type: NamedType, from: string, capability: string): void;
25
+ has(id: string): boolean;
26
+ list(filterArgs?: Filters): Array<NamedType>;
27
+ info(id: string): TypeMetadata | undefined;
28
+ }