@embeddable.com/sdk-core 3.9.4 → 3.9.6
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/defineConfig.d.ts +3 -1
- package/lib/index.esm.js +69 -15
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +69 -15
- package/lib/index.js.map +1 -1
- package/lib/push.d.ts +2 -1
- package/lib/validate.d.ts +1 -0
- package/package.json +1 -1
- package/src/defineConfig.test.ts +2 -0
- package/src/defineConfig.ts +11 -0
- package/src/dev.test.ts +1 -0
- package/src/dev.ts +15 -6
- package/src/push.ts +13 -5
- package/src/validate.test.ts +36 -1
- package/src/validate.ts +69 -4
- package/templates/component.tsx.template +4 -1
package/lib/push.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const CUBE_FILES: RegExp;
|
|
2
|
+
export declare const PRESET_FILES: RegExp;
|
|
2
3
|
declare const _default: () => Promise<void>;
|
|
3
4
|
export default _default;
|
|
4
5
|
export declare function archive(ctx: any, yamlFiles: [string, string][], isDev?: boolean): Promise<unknown>;
|
package/lib/validate.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ declare const _default: (ctx: any, exitIfInvalid?: boolean) => Promise<boolean>;
|
|
|
2
2
|
export default _default;
|
|
3
3
|
export declare function dataModelsValidation(filesList: [string, string][]): Promise<string[]>;
|
|
4
4
|
export declare function securityContextValidation(filesList: [string, string][]): Promise<string[]>;
|
|
5
|
+
export declare function clientContextValidation(filesList: [string, string][]): Promise<string[]>;
|
package/package.json
CHANGED
package/src/defineConfig.test.ts
CHANGED
|
@@ -25,6 +25,7 @@ const baseConfigArgs = {
|
|
|
25
25
|
rollbarAccessToken: "rollbarAccessToken",
|
|
26
26
|
previewBaseUrl: "previewBaseUrl",
|
|
27
27
|
modelsSrc: "modelsSrc",
|
|
28
|
+
presetsSrc: "presetsSrc",
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
describe("defineConfig", () => {
|
|
@@ -55,6 +56,7 @@ describe("defineConfig", () => {
|
|
|
55
56
|
"errorFallbackComponent": "/embeddable-sdk/packages/core-sdk",
|
|
56
57
|
"globalCss": "/embeddable-sdk/packages/core-sdk",
|
|
57
58
|
"modelsSrc": "/embeddable-sdk/packages/core-sdk",
|
|
59
|
+
"presetsSrc": "/embeddable-sdk/packages/core-sdk",
|
|
58
60
|
"rollupOptions": {},
|
|
59
61
|
"rootDir": "/embeddable-sdk/packages/core-sdk",
|
|
60
62
|
"srcDir": "/embeddable-sdk/packages/core-sdk",
|
package/src/defineConfig.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type EmbeddableConfig = {
|
|
|
19
19
|
previewBaseUrl?: string;
|
|
20
20
|
componentsSrc?: string;
|
|
21
21
|
modelsSrc?: string;
|
|
22
|
+
presetsSrc?: string;
|
|
22
23
|
globalCss?: string;
|
|
23
24
|
viteConfig?: {
|
|
24
25
|
resolve?: {
|
|
@@ -39,6 +40,7 @@ export default ({
|
|
|
39
40
|
rollbarAccessToken,
|
|
40
41
|
previewBaseUrl,
|
|
41
42
|
modelsSrc = "src",
|
|
43
|
+
presetsSrc = "src",
|
|
42
44
|
componentsSrc = "src",
|
|
43
45
|
globalCss = "src/global.css",
|
|
44
46
|
viteConfig = {},
|
|
@@ -65,6 +67,14 @@ export default ({
|
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
if (presetsSrc && !path.isAbsolute(presetsSrc)) {
|
|
71
|
+
presetsSrc = path.resolve(clientRoot, presetsSrc);
|
|
72
|
+
|
|
73
|
+
if (!existsSync(presetsSrc)) {
|
|
74
|
+
throw new Error(`presetsSrc directory ${presetsSrc} does not exist`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
return {
|
|
69
79
|
core: {
|
|
70
80
|
rootDir: coreRoot,
|
|
@@ -75,6 +85,7 @@ export default ({
|
|
|
75
85
|
rootDir: clientRoot,
|
|
76
86
|
srcDir: path.resolve(clientRoot, componentsSrc),
|
|
77
87
|
modelsSrc: modelsSrc ? path.resolve(clientRoot, modelsSrc) : undefined,
|
|
88
|
+
presetsSrc: presetsSrc ? path.resolve(clientRoot, presetsSrc) : undefined,
|
|
78
89
|
buildDir: path.resolve(clientRoot, ".embeddable-build"),
|
|
79
90
|
tmpDir: path.resolve(clientRoot, ".embeddable-tmp"),
|
|
80
91
|
globalCss: path.resolve(clientRoot, globalCss),
|
package/src/dev.test.ts
CHANGED
package/src/dev.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { FSWatcher } from "chokidar";
|
|
|
21
21
|
import { getToken, default as login } from "./login";
|
|
22
22
|
import axios from "axios";
|
|
23
23
|
import { findFiles } from "@embeddable.com/sdk-utils";
|
|
24
|
-
import { archive,
|
|
24
|
+
import { archive, PRESET_FILES, CUBE_FILES, sendBuild } from "./push";
|
|
25
25
|
import validate from "./validate";
|
|
26
26
|
import { checkNodeVersion } from "./utils";
|
|
27
27
|
import { createManifest } from "./cleanup";
|
|
@@ -191,7 +191,7 @@ export default async () => {
|
|
|
191
191
|
editorsMetaFileName: "embeddable-editors-meta.js",
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
-
await
|
|
194
|
+
await sendDataModelsAndContextsChanges(config);
|
|
195
195
|
|
|
196
196
|
for (const getPlugin of config.plugins) {
|
|
197
197
|
const plugin = getPlugin();
|
|
@@ -274,11 +274,14 @@ const onBundleBuildEnd = async (ctx: any) => {
|
|
|
274
274
|
|
|
275
275
|
const dataModelAndSecurityContextWatcher = (ctx: any): FSWatcher => {
|
|
276
276
|
const fsWatcher = chokidar.watch(
|
|
277
|
-
[
|
|
277
|
+
[
|
|
278
|
+
path.resolve(ctx.client.modelsSrc, "**/*.{cube}.{yaml,yml,js}"),
|
|
279
|
+
path.resolve(ctx.client.presetsSrc, "**/*.{sc,cc}.{yaml,yml}"),
|
|
280
|
+
],
|
|
278
281
|
chokidarWatchOptions,
|
|
279
282
|
);
|
|
280
283
|
fsWatcher.on("all", async () => {
|
|
281
|
-
await
|
|
284
|
+
await sendDataModelsAndContextsChanges(ctx);
|
|
282
285
|
});
|
|
283
286
|
|
|
284
287
|
return fsWatcher;
|
|
@@ -294,7 +297,7 @@ const globalCssWatcher = (ctx: any): FSWatcher => {
|
|
|
294
297
|
return fsWatcher;
|
|
295
298
|
};
|
|
296
299
|
|
|
297
|
-
const
|
|
300
|
+
const sendDataModelsAndContextsChanges = async (ctx: any) => {
|
|
298
301
|
sendMessage("dataModelsAndOrSecurityContextUpdateStart");
|
|
299
302
|
const isValid = await validate(ctx, false);
|
|
300
303
|
if (isValid) {
|
|
@@ -302,7 +305,13 @@ const sendDataModelsAndSecurityContextsChanges = async (ctx: any) => {
|
|
|
302
305
|
const sending = ora(
|
|
303
306
|
"Synchronising data models and/or security contexts...",
|
|
304
307
|
).start();
|
|
305
|
-
const
|
|
308
|
+
const cubeFilesList = await findFiles(ctx.client.modelsSrc, CUBE_FILES);
|
|
309
|
+
const contextFilesList = await findFiles(
|
|
310
|
+
ctx.client.presetsSrc,
|
|
311
|
+
PRESET_FILES,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const filesList = [...cubeFilesList, ...contextFilesList];
|
|
306
315
|
|
|
307
316
|
// add manifest to the archive
|
|
308
317
|
filesList.push([
|
package/src/push.ts
CHANGED
|
@@ -15,8 +15,11 @@ import { getToken } from "./login";
|
|
|
15
15
|
import { checkBuildSuccess, checkNodeVersion, getArgumentByKey } from "./utils";
|
|
16
16
|
import { selectWorkspace } from "./workspaceUtils";
|
|
17
17
|
|
|
18
|
-
// grab
|
|
19
|
-
export const
|
|
18
|
+
// grab cube files
|
|
19
|
+
export const CUBE_FILES = /^(.*)\.cube\.(ya?ml|js)$/;
|
|
20
|
+
|
|
21
|
+
// grab security context and client context files
|
|
22
|
+
export const PRESET_FILES = /^(.*)\.(sc|cc)\.ya?ml$/;
|
|
20
23
|
|
|
21
24
|
let ora: any;
|
|
22
25
|
export default async () => {
|
|
@@ -134,12 +137,17 @@ async function verify(ctx: any) {
|
|
|
134
137
|
async function buildArchive(config: any) {
|
|
135
138
|
const spinnerArchive = ora("Building...").start();
|
|
136
139
|
|
|
137
|
-
const
|
|
140
|
+
const cubeFilesList = await findFiles(
|
|
138
141
|
config.client.modelsSrc || config.client.srcDir,
|
|
139
|
-
|
|
142
|
+
CUBE_FILES,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const contextFilesList = await findFiles(
|
|
146
|
+
config.client.presetsSrc || config.client.srcDir,
|
|
147
|
+
PRESET_FILES,
|
|
140
148
|
);
|
|
141
149
|
|
|
142
|
-
await archive(config,
|
|
150
|
+
await archive(config, [...cubeFilesList, ...contextFilesList]);
|
|
143
151
|
return spinnerArchive.succeed("Bundling completed");
|
|
144
152
|
}
|
|
145
153
|
|
package/src/validate.test.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
clientContextValidation,
|
|
3
|
+
dataModelsValidation,
|
|
4
|
+
securityContextValidation,
|
|
5
|
+
} from "./validate";
|
|
2
6
|
import * as fs from "node:fs/promises";
|
|
3
7
|
|
|
4
8
|
const startMock = {
|
|
@@ -35,6 +39,11 @@ const securityContextYaml = `
|
|
|
35
39
|
country: United States
|
|
36
40
|
environment: default`;
|
|
37
41
|
|
|
42
|
+
const clientContextYaml = `
|
|
43
|
+
- name: blue
|
|
44
|
+
clientContext:
|
|
45
|
+
color: blue`;
|
|
46
|
+
|
|
38
47
|
vi.mock("ora", () => ({
|
|
39
48
|
default: () => ({
|
|
40
49
|
start: vi.fn().mockImplementation(() => startMock),
|
|
@@ -121,4 +130,30 @@ describe("validate", () => {
|
|
|
121
130
|
]);
|
|
122
131
|
});
|
|
123
132
|
});
|
|
133
|
+
|
|
134
|
+
describe("clientContextValidation", () => {
|
|
135
|
+
it("should return an empty array if the client context is valid", async () => {
|
|
136
|
+
vi.mocked(fs.readFile).mockImplementation(async () => {
|
|
137
|
+
return clientContextYaml;
|
|
138
|
+
});
|
|
139
|
+
const filesList: [string, string][] = [
|
|
140
|
+
["valid-client-context.json", "path/to/file"],
|
|
141
|
+
];
|
|
142
|
+
const result = await clientContextValidation(filesList);
|
|
143
|
+
expect(result).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should return an array of error messages if the client context is invalid", async () => {
|
|
147
|
+
vi.mocked(fs.readFile).mockImplementation(async () => {
|
|
148
|
+
return `${clientContextYaml} ${clientContextYaml}`;
|
|
149
|
+
});
|
|
150
|
+
const filesList: [string, string][] = [
|
|
151
|
+
["invalid-client-context.json", "path/to/file"],
|
|
152
|
+
];
|
|
153
|
+
const result = await clientContextValidation(filesList);
|
|
154
|
+
expect(result).toEqual([
|
|
155
|
+
'path/to/file: client context with name "blue" already exists',
|
|
156
|
+
]);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
124
159
|
});
|
package/src/validate.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { checkNodeVersion } from "./utils";
|
|
|
6
6
|
|
|
7
7
|
const CUBE_YAML_FILE_REGEX = /^(.*)\.cube\.ya?ml$/;
|
|
8
8
|
const SECURITY_CONTEXT_FILE_REGEX = /^(.*)\.sc\.ya?ml$/;
|
|
9
|
+
const CLIENT_CONTEXT_FILE_REGEX = /^(.*)\.cc\.ya?ml$/;
|
|
9
10
|
|
|
10
11
|
export default async (ctx: any, exitIfInvalid = true) => {
|
|
11
12
|
checkNodeVersion();
|
|
@@ -13,13 +14,20 @@ export default async (ctx: any, exitIfInvalid = true) => {
|
|
|
13
14
|
|
|
14
15
|
const spinnerValidate = ora("Data model validation...").start();
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
const securityContextFilesList = await findFiles(
|
|
17
|
+
const cubeFilesList = await findFiles(
|
|
18
18
|
ctx.client.modelsSrc || ctx.client.srcDir,
|
|
19
|
+
CUBE_YAML_FILE_REGEX,
|
|
20
|
+
);
|
|
21
|
+
const securityContextFilesList = await findFiles(
|
|
22
|
+
ctx.client.presetsSrc || ctx.client.srcDir,
|
|
19
23
|
SECURITY_CONTEXT_FILE_REGEX,
|
|
20
24
|
);
|
|
25
|
+
const clientContextFilesList = await findFiles(
|
|
26
|
+
ctx.client.presetsSrc || ctx.client.srcDir,
|
|
27
|
+
CLIENT_CONTEXT_FILE_REGEX,
|
|
28
|
+
);
|
|
21
29
|
|
|
22
|
-
const dataModelErrors = await dataModelsValidation(
|
|
30
|
+
const dataModelErrors = await dataModelsValidation(cubeFilesList);
|
|
23
31
|
|
|
24
32
|
if (dataModelErrors.length) {
|
|
25
33
|
spinnerValidate.fail("One or more cube.yaml files are invalid:");
|
|
@@ -39,6 +47,10 @@ export default async (ctx: any, exitIfInvalid = true) => {
|
|
|
39
47
|
securityContextFilesList,
|
|
40
48
|
);
|
|
41
49
|
|
|
50
|
+
const clientContextErrors = await clientContextValidation(
|
|
51
|
+
clientContextFilesList,
|
|
52
|
+
);
|
|
53
|
+
|
|
42
54
|
if (securityContextErrors.length) {
|
|
43
55
|
spinnerValidate.fail("One or more security context files are invalid:");
|
|
44
56
|
|
|
@@ -51,7 +63,23 @@ export default async (ctx: any, exitIfInvalid = true) => {
|
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
if (clientContextErrors.length) {
|
|
67
|
+
spinnerValidate.fail("One or more client context files are invalid:");
|
|
68
|
+
|
|
69
|
+
clientContextErrors.forEach((errorMessage) =>
|
|
70
|
+
spinnerValidate.info(errorMessage),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (exitIfInvalid) {
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
dataModelErrors.length === 0 &&
|
|
80
|
+
securityContextErrors.length === 0 &&
|
|
81
|
+
clientContextErrors.length === 0
|
|
82
|
+
);
|
|
55
83
|
};
|
|
56
84
|
|
|
57
85
|
export async function dataModelsValidation(filesList: [string, string][]) {
|
|
@@ -118,6 +146,36 @@ export async function securityContextValidation(filesList: [string, string][]) {
|
|
|
118
146
|
return errors;
|
|
119
147
|
}
|
|
120
148
|
|
|
149
|
+
export async function clientContextValidation(filesList: [string, string][]) {
|
|
150
|
+
const errors: string[] = [];
|
|
151
|
+
|
|
152
|
+
const nameSet = new Set<string>();
|
|
153
|
+
for (const [_, filePath] of filesList) {
|
|
154
|
+
const fileContentRaw = await fs.readFile(filePath, "utf8");
|
|
155
|
+
|
|
156
|
+
const cube = YAML.parse(fileContentRaw);
|
|
157
|
+
|
|
158
|
+
cube.forEach((item: { name: string }) => {
|
|
159
|
+
if (nameSet.has(item.name)) {
|
|
160
|
+
errors.push(
|
|
161
|
+
`${filePath}: client context with name "${item.name}" already exists`,
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
nameSet.add(item.name);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const safeParse = clientContextSchema.safeParse(cube);
|
|
169
|
+
if (!safeParse.success) {
|
|
170
|
+
errorFormatter(safeParse.error.issues).forEach((error) => {
|
|
171
|
+
errors.push(`${filePath}: ${error}`);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return errors;
|
|
177
|
+
}
|
|
178
|
+
|
|
121
179
|
enum MeasureTypeEnum {
|
|
122
180
|
string = "string",
|
|
123
181
|
time = "time",
|
|
@@ -194,3 +252,10 @@ const securityContextSchema = z.array(
|
|
|
194
252
|
securityContext: z.object({}), // can be any object
|
|
195
253
|
}),
|
|
196
254
|
);
|
|
255
|
+
|
|
256
|
+
const clientContextSchema = z.array(
|
|
257
|
+
z.object({
|
|
258
|
+
name: z.string(),
|
|
259
|
+
clientContext: z.object({}), // can be any object
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
@@ -13,6 +13,7 @@ export class EmbeddableComponent {
|
|
|
13
13
|
|
|
14
14
|
@Prop() componentName: string;
|
|
15
15
|
@Prop() props: string = '{}';
|
|
16
|
+
@Prop() clientContext: string = '{}';
|
|
16
17
|
|
|
17
18
|
@Event({
|
|
18
19
|
eventName: 'componentLoaded',
|
|
@@ -21,6 +22,7 @@ export class EmbeddableComponent {
|
|
|
21
22
|
bubbles: true,
|
|
22
23
|
}) componentDidLoadEvent: EventEmitter<any>;
|
|
23
24
|
|
|
25
|
+
@Watch('clientContext')
|
|
24
26
|
@Watch('componentName')
|
|
25
27
|
watchComponentName() {
|
|
26
28
|
this.handlePops();
|
|
@@ -77,7 +79,8 @@ export class EmbeddableComponent {
|
|
|
77
79
|
render(
|
|
78
80
|
this.rootElement,
|
|
79
81
|
this.componentName,
|
|
80
|
-
JSON.parse(this.props)
|
|
82
|
+
JSON.parse(this.props),
|
|
83
|
+
JSON.parse(this.clientContext)
|
|
81
84
|
);
|
|
82
85
|
}
|
|
83
86
|
}
|