@superblocksteam/util 2.0.162 → 2.0.163-next.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/dist/api-files/api.d.mts +8 -0
- package/dist/api-files/api.d.mts.map +1 -0
- package/dist/api-files/api.mjs +262 -0
- package/dist/api-files/api.mjs.map +1 -0
- package/dist/api-files/constants.d.ts +6 -0
- package/dist/api-files/constants.d.ts.map +1 -0
- package/dist/api-files/constants.js +9 -0
- package/dist/api-files/constants.js.map +1 -0
- package/dist/api-files/index.d.ts +8 -0
- package/dist/api-files/index.d.ts.map +1 -0
- package/dist/api-files/index.js +18 -0
- package/dist/api-files/index.js.map +1 -0
- package/dist/api-files/types.d.ts +115 -0
- package/dist/api-files/types.d.ts.map +1 -0
- package/dist/api-files/types.js +14 -0
- package/dist/api-files/types.js.map +1 -0
- package/dist/component-configs.d.ts +6 -0
- package/dist/component-configs.d.ts.map +1 -0
- package/dist/component-configs.js +178 -0
- package/dist/component-configs.js.map +1 -0
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +37 -0
- package/dist/constants.js.map +1 -0
- package/dist/data-types.d.ts +8 -0
- package/dist/data-types.d.ts.map +1 -0
- package/dist/data-types.js +22 -0
- package/dist/data-types.js.map +1 -0
- package/dist/events.d.ts +13 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +17 -0
- package/dist/events.js.map +1 -0
- package/dist/file-uploader.d.ts +3 -0
- package/dist/file-uploader.d.ts.map +1 -0
- package/dist/file-uploader.js +41 -0
- package/dist/file-uploader.js.map +1 -0
- package/dist/file-uploader.test.d.ts +2 -0
- package/dist/file-uploader.test.d.ts.map +1 -0
- package/dist/file-uploader.test.js +43 -0
- package/dist/file-uploader.test.js.map +1 -0
- package/dist/generate-component-types.d.ts +3 -0
- package/dist/generate-component-types.d.ts.map +1 -0
- package/dist/generate-component-types.js +45 -0
- package/dist/generate-component-types.js.map +1 -0
- package/dist/identifiers.d.ts +9 -0
- package/dist/identifiers.d.ts.map +1 -0
- package/dist/identifiers.js +68 -0
- package/dist/identifiers.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/localGitRepoState.d.ts +20 -0
- package/dist/localGitRepoState.d.ts.map +1 -0
- package/dist/localGitRepoState.js +3 -0
- package/dist/localGitRepoState.js.map +1 -0
- package/dist/login.d.ts +17 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +74 -0
- package/dist/login.js.map +1 -0
- package/dist/mask-unix-signals.d.ts +28 -0
- package/dist/mask-unix-signals.d.ts.map +1 -0
- package/dist/mask-unix-signals.js +81 -0
- package/dist/mask-unix-signals.js.map +1 -0
- package/dist/resource-configs.d.ts +73 -0
- package/dist/resource-configs.d.ts.map +1 -0
- package/dist/resource-configs.js +224 -0
- package/dist/resource-configs.js.map +1 -0
- package/dist/token-manager.d.ts +22 -0
- package/dist/token-manager.d.ts.map +1 -0
- package/dist/token-manager.js +3 -0
- package/dist/token-manager.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/unreachable.d.ts +2 -0
- package/dist/unreachable.d.ts.map +1 -0
- package/dist/unreachable.js +7 -0
- package/dist/unreachable.js.map +1 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +152 -0
- package/dist/validation.js.map +1 -0
- package/package.json +5 -2
- package/eslint.config.mjs +0 -67
- package/lint-staged.config.mjs +0 -6
- package/src/api-files/api.mts +0 -506
- package/src/api-files/constants.ts +0 -6
- package/src/api-files/index.ts +0 -22
- package/src/api-files/types.ts +0 -131
- package/src/component-configs.ts +0 -163
- package/src/constants.ts +0 -34
- package/src/data-types.ts +0 -25
- package/src/events.ts +0 -12
- package/src/file-uploader.test.ts +0 -69
- package/src/file-uploader.ts +0 -42
- package/src/generate-component-types.ts +0 -53
- package/src/identifiers.ts +0 -70
- package/src/localGitRepoState.ts +0 -20
- package/src/login.ts +0 -82
- package/src/mask-unix-signals.ts +0 -86
- package/src/resource-configs.ts +0 -356
- package/src/token-manager.ts +0 -26
- package/src/types.ts +0 -138
- package/src/unreachable.ts +0 -5
- package/src/validation.ts +0 -155
- package/tsconfig.json +0 -21
- package/turbo.json +0 -9
- package/vitest.config.ts +0 -7
package/src/component-configs.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import * as util from "node:util";
|
|
5
|
-
|
|
6
|
-
import fs from "fs-extra";
|
|
7
|
-
import mimeTypes from "mime-types";
|
|
8
|
-
|
|
9
|
-
import { CUSTOM_COMPONENTS_PATH } from "./constants.js";
|
|
10
|
-
import { generateComponentTypesFile } from "./generate-component-types.js";
|
|
11
|
-
import { validateCustomComponents } from "./validation.js";
|
|
12
|
-
|
|
13
|
-
const asyncExecFile = util.promisify(execFile);
|
|
14
|
-
|
|
15
|
-
async function compileTypeScript(
|
|
16
|
-
inputFilePath: string,
|
|
17
|
-
outputDir: string,
|
|
18
|
-
): Promise<{ output?: string }> {
|
|
19
|
-
try {
|
|
20
|
-
const tscPath = require.resolve("typescript/bin/tsc");
|
|
21
|
-
if (tscPath) {
|
|
22
|
-
const result = await asyncExecFile("node", [
|
|
23
|
-
tscPath,
|
|
24
|
-
path.resolve(inputFilePath),
|
|
25
|
-
"--outDir",
|
|
26
|
-
outputDir,
|
|
27
|
-
"--jsx",
|
|
28
|
-
"react-jsx",
|
|
29
|
-
"--target",
|
|
30
|
-
"es2019",
|
|
31
|
-
"--esModuleInterop",
|
|
32
|
-
"true",
|
|
33
|
-
"--moduleResolution",
|
|
34
|
-
"node",
|
|
35
|
-
"--module",
|
|
36
|
-
"es2022",
|
|
37
|
-
"--strict",
|
|
38
|
-
"true",
|
|
39
|
-
"--skipLibCheck",
|
|
40
|
-
"true",
|
|
41
|
-
]);
|
|
42
|
-
if (result.stdout || result.stderr) {
|
|
43
|
-
return {
|
|
44
|
-
output: result.stdout || result.stderr,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} catch (error: any) {
|
|
49
|
-
return {
|
|
50
|
-
output: error.stdout || error.stderr,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return {};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function getFolderPaths() {
|
|
57
|
-
try {
|
|
58
|
-
const folderPaths = await fs.readdir(CUSTOM_COMPONENTS_PATH, {
|
|
59
|
-
withFileTypes: true,
|
|
60
|
-
});
|
|
61
|
-
// filter out any non-directory items
|
|
62
|
-
const directories = folderPaths.filter(
|
|
63
|
-
(dirent) =>
|
|
64
|
-
dirent.isDirectory() ||
|
|
65
|
-
(dirent.isSymbolicLink() &&
|
|
66
|
-
fs
|
|
67
|
-
.statSync(path.join(CUSTOM_COMPONENTS_PATH, dirent.name))
|
|
68
|
-
.isDirectory()),
|
|
69
|
-
);
|
|
70
|
-
// map each directory to its path
|
|
71
|
-
const folderPathsArray = directories.map((dirent) =>
|
|
72
|
-
path.join(CUSTOM_COMPONENTS_PATH, dirent.name),
|
|
73
|
-
);
|
|
74
|
-
return folderPathsArray;
|
|
75
|
-
} catch {
|
|
76
|
-
throw new Error(
|
|
77
|
-
"Could not access component directory, check your permissions",
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export async function getComponentConfigs(
|
|
83
|
-
generateTypesFiles: boolean,
|
|
84
|
-
): Promise<{
|
|
85
|
-
configs: Record<string, any>;
|
|
86
|
-
hasError: boolean;
|
|
87
|
-
}> {
|
|
88
|
-
const folderPaths = await getFolderPaths();
|
|
89
|
-
let hasError = false;
|
|
90
|
-
const promiseConfigFiles = folderPaths.map(async (ccpath) => {
|
|
91
|
-
const absPath = path.resolve(ccpath);
|
|
92
|
-
try {
|
|
93
|
-
const config = await fs.readJSON(`${absPath}/config.json`);
|
|
94
|
-
console.log(`Found component in ${absPath}`);
|
|
95
|
-
return { ccpath, config };
|
|
96
|
-
} catch {
|
|
97
|
-
// Noop because there is a fallback
|
|
98
|
-
}
|
|
99
|
-
// This is a tad complex to explain this hack but here's the context:
|
|
100
|
-
// We want to be able to hot reload the config.ts file, but we can't import it directly
|
|
101
|
-
// because it is going to get cached in the module cache and we dont have access to it to clear it
|
|
102
|
-
// so instead we copy the file to a new location with a random name and import that
|
|
103
|
-
// this is technically a memory leak, but you shouldnt really have a hot module server running forever
|
|
104
|
-
const rawPath = `${ccpath}/config.ts`;
|
|
105
|
-
const fileName = path.resolve(rawPath);
|
|
106
|
-
const outputDirectory = path.resolve(
|
|
107
|
-
`${os.tmpdir()}/superblocks/${ccpath}/config.timestamp-${Date.now()}-${Math.random()
|
|
108
|
-
.toString(16)
|
|
109
|
-
.slice(2)}`,
|
|
110
|
-
);
|
|
111
|
-
await fs.ensureDir(outputDirectory);
|
|
112
|
-
const compilationResult = await compileTypeScript(
|
|
113
|
-
fileName,
|
|
114
|
-
outputDirectory,
|
|
115
|
-
);
|
|
116
|
-
if (compilationResult.output) {
|
|
117
|
-
hasError = true;
|
|
118
|
-
if (compilationResult.output.includes(`Cannot find module`)) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
"Could not find a module in node_modules. You likely need to run 'npm install'",
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
throw new Error(compilationResult.output);
|
|
124
|
-
}
|
|
125
|
-
console.log(`Typescript compiled to ${ccpath}`);
|
|
126
|
-
const config = (await import(outputDirectory + "/config.js")).default;
|
|
127
|
-
|
|
128
|
-
const isValid = validateCustomComponents(config);
|
|
129
|
-
if (!isValid.valid) {
|
|
130
|
-
// Not throwing because we don't need a stack trace here
|
|
131
|
-
console.log(`Invalid config.ts file found`);
|
|
132
|
-
console.log(isValid.message);
|
|
133
|
-
hasError = true;
|
|
134
|
-
}
|
|
135
|
-
return { ccpath, config };
|
|
136
|
-
});
|
|
137
|
-
const configFiles = await Promise.all(promiseConfigFiles);
|
|
138
|
-
if (generateTypesFiles) {
|
|
139
|
-
for (const { config, ccpath } of configFiles) {
|
|
140
|
-
const typesFileContents = generateComponentTypesFile(config);
|
|
141
|
-
await fs.writeFile(`${ccpath}/types.ts`, typesFileContents);
|
|
142
|
-
console.log(`Generated ${ccpath}/types.ts`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const configs = configFiles
|
|
146
|
-
.filter((component) => component.config !== null)
|
|
147
|
-
.reduce((acc: Record<string, any>, { config }) => {
|
|
148
|
-
acc[config.id] = config;
|
|
149
|
-
return acc;
|
|
150
|
-
}, {});
|
|
151
|
-
return {
|
|
152
|
-
configs,
|
|
153
|
-
hasError,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export const getContentType = (filename: string): string => {
|
|
158
|
-
const mimeType = mimeTypes.lookup(filename);
|
|
159
|
-
if (!mimeType) {
|
|
160
|
-
return "";
|
|
161
|
-
}
|
|
162
|
-
return mimeType as string;
|
|
163
|
-
};
|
package/src/constants.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export const SUPERBLOCKS_HOME_FOLDER_NAME = ".superblocks";
|
|
2
|
-
export const TOKEN_CONFIG_PATH = ".superblocks/auth.json";
|
|
3
|
-
export const RESOURCE_CONFIG_PATH = ".superblocks/superblocks.json";
|
|
4
|
-
export const DEV_ENVIRONMENT_CONFIG_PATH = ".superblocks/dev-environment.json";
|
|
5
|
-
|
|
6
|
-
export const CUSTOM_COMPONENTS_PATH = "components";
|
|
7
|
-
|
|
8
|
-
export class FileAccessError extends Error {
|
|
9
|
-
constructor(message: string) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.name = "FileAccessError";
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class NotFoundError extends Error {
|
|
16
|
-
constructor(message: string) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = "FileNotFoundError";
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class BadRequestError extends Error {
|
|
23
|
-
constructor(message: string) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = "BadRequestError";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class ForbiddenError extends Error {
|
|
30
|
-
constructor(message: string) {
|
|
31
|
-
super(message);
|
|
32
|
-
this.name = "ForbiddenError";
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/data-types.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export type DataType = "string" | "number" | "boolean" | "any";
|
|
2
|
-
|
|
3
|
-
interface TypeInfo {
|
|
4
|
-
tsType: string;
|
|
5
|
-
prompt: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const dataTypeDefinions: Record<DataType, TypeInfo> = {
|
|
9
|
-
string: {
|
|
10
|
-
tsType: "string",
|
|
11
|
-
prompt: "string",
|
|
12
|
-
},
|
|
13
|
-
number: {
|
|
14
|
-
tsType: "number",
|
|
15
|
-
prompt: "number",
|
|
16
|
-
},
|
|
17
|
-
boolean: {
|
|
18
|
-
tsType: "boolean",
|
|
19
|
-
prompt: "boolean",
|
|
20
|
-
},
|
|
21
|
-
any: {
|
|
22
|
-
tsType: "any",
|
|
23
|
-
prompt: "any (raw js expression)",
|
|
24
|
-
},
|
|
25
|
-
};
|
package/src/events.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { getBucketeerUrlFromSuperblocksUrl } from "./file-uploader.js";
|
|
4
|
-
|
|
5
|
-
describe("getBucketeerUrlFromSuperblocksUrl", () => {
|
|
6
|
-
let originalUrl: string | undefined;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
originalUrl = process.env.SUPERBLOCKS_BUCKETEER_URL;
|
|
10
|
-
delete process.env.SUPERBLOCKS_BUCKETEER_URL;
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
if (originalUrl === undefined) {
|
|
15
|
-
delete process.env.SUPERBLOCKS_BUCKETEER_URL;
|
|
16
|
-
} else {
|
|
17
|
-
process.env.SUPERBLOCKS_BUCKETEER_URL = originalUrl;
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("returns the default localhost bucketeer URL for localhost servers", () => {
|
|
22
|
-
expect(getBucketeerUrlFromSuperblocksUrl("http://localhost:8080")).toBe(
|
|
23
|
-
"http://localhost:8030",
|
|
24
|
-
);
|
|
25
|
-
expect(getBucketeerUrlFromSuperblocksUrl("http://127.0.0.1:8080")).toBe(
|
|
26
|
-
"http://localhost:8030",
|
|
27
|
-
);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("honors SUPERBLOCKS_BUCKETEER_URL for localhost servers (slot-aware worktrees)", () => {
|
|
31
|
-
process.env.SUPERBLOCKS_BUCKETEER_URL = "http://localhost:8330";
|
|
32
|
-
expect(getBucketeerUrlFromSuperblocksUrl("http://localhost:8380")).toBe(
|
|
33
|
-
"http://localhost:8330",
|
|
34
|
-
);
|
|
35
|
-
expect(getBucketeerUrlFromSuperblocksUrl("http://127.0.0.1:8380")).toBe(
|
|
36
|
-
"http://localhost:8330",
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("falls back to localhost default when the input URL is unparseable", () => {
|
|
41
|
-
expect(getBucketeerUrlFromSuperblocksUrl("not a url")).toBe(
|
|
42
|
-
"http://localhost:8030",
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("uses SUPERBLOCKS_BUCKETEER_URL when the input URL is unparseable", () => {
|
|
47
|
-
process.env.SUPERBLOCKS_BUCKETEER_URL = "http://localhost:8330";
|
|
48
|
-
expect(getBucketeerUrlFromSuperblocksUrl("not a url")).toBe(
|
|
49
|
-
"http://localhost:8330",
|
|
50
|
-
);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("ignores SUPERBLOCKS_BUCKETEER_URL for non-localhost hostnames", () => {
|
|
54
|
-
process.env.SUPERBLOCKS_BUCKETEER_URL = "http://localhost:8330";
|
|
55
|
-
|
|
56
|
-
expect(
|
|
57
|
-
getBucketeerUrlFromSuperblocksUrl("https://app.superblocks.com"),
|
|
58
|
-
).toBe("https://bucketeer.superblocks.com");
|
|
59
|
-
expect(
|
|
60
|
-
getBucketeerUrlFromSuperblocksUrl("https://staging.superblocks.com"),
|
|
61
|
-
).toBe("https://bucketeer.staging.superblocks.com");
|
|
62
|
-
expect(
|
|
63
|
-
getBucketeerUrlFromSuperblocksUrl("https://eu.superblocks.com"),
|
|
64
|
-
).toBe("https://bucketeer.eu.superblocks.com");
|
|
65
|
-
expect(
|
|
66
|
-
getBucketeerUrlFromSuperblocksUrl("https://pr-123.superblocks.dev"),
|
|
67
|
-
).toBe("https://bucketeer.dev.superblocks.com");
|
|
68
|
-
});
|
|
69
|
-
});
|
package/src/file-uploader.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export const COMPONENT_EVENT_HEADER = "x-superblocks-component-event";
|
|
2
|
-
|
|
3
|
-
// SUPERBLOCKS_BUCKETEER_URL lets slot-N Tilt worktrees override the default
|
|
4
|
-
// localhost bucketeer URL (http://localhost:8030) when the server runs on a
|
|
5
|
-
// different slot offset. The local-orch Tilt profile injects this variable
|
|
6
|
-
// for the server's serve env.
|
|
7
|
-
const localhostBucketeerUrl = (): string =>
|
|
8
|
-
process.env.SUPERBLOCKS_BUCKETEER_URL ?? "http://localhost:8030";
|
|
9
|
-
|
|
10
|
-
// retrieves bucketeer base URL given the superblocks server base URL
|
|
11
|
-
export const getBucketeerUrlFromSuperblocksUrl = (
|
|
12
|
-
superblocksBaseUrl: string,
|
|
13
|
-
): string => {
|
|
14
|
-
let hostname: string;
|
|
15
|
-
try {
|
|
16
|
-
hostname = new URL(superblocksBaseUrl).hostname;
|
|
17
|
-
} catch {
|
|
18
|
-
return localhostBucketeerUrl();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (/^pr-.*\.superblocks\.dev$/u.test(hostname)) {
|
|
22
|
-
return "https://bucketeer.dev.superblocks.com";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
switch (hostname) {
|
|
26
|
-
case "eu.superblocks.com":
|
|
27
|
-
case "prod-eu.superblocks.com":
|
|
28
|
-
return "https://bucketeer.eu.superblocks.com";
|
|
29
|
-
case "app.staging.superblocks.com":
|
|
30
|
-
case "staging.superblocks.com":
|
|
31
|
-
return "https://bucketeer.staging.superblocks.com";
|
|
32
|
-
case "app.superblocks.com":
|
|
33
|
-
case "app.superblockshq.com":
|
|
34
|
-
return "https://bucketeer.superblocks.com";
|
|
35
|
-
case "dev.superblocks.com":
|
|
36
|
-
return "https://bucketeer.dev.superblocks.com";
|
|
37
|
-
case "127.0.0.1":
|
|
38
|
-
case "localhost":
|
|
39
|
-
default:
|
|
40
|
-
return localhostBucketeerUrl();
|
|
41
|
-
}
|
|
42
|
-
};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { dataTypeDefinions, type DataType } from "./data-types.js";
|
|
2
|
-
import type { ComponentConfig } from "./types.js";
|
|
3
|
-
|
|
4
|
-
const indent = (str: string, spaces: number) => {
|
|
5
|
-
const spacesString = " ".repeat(spaces);
|
|
6
|
-
|
|
7
|
-
return str
|
|
8
|
-
.split("\n")
|
|
9
|
-
.map((line) => `${spacesString}${line}`)
|
|
10
|
-
.join("\n");
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function renderInputTypeAsTS(
|
|
14
|
-
name: string,
|
|
15
|
-
type: DataType,
|
|
16
|
-
trailingChars: string,
|
|
17
|
-
indentSpaces: number,
|
|
18
|
-
) {
|
|
19
|
-
let { tsType } = dataTypeDefinions[type];
|
|
20
|
-
if (["number", "string"].includes(type)) {
|
|
21
|
-
tsType += " | null";
|
|
22
|
-
}
|
|
23
|
-
return indent(`${name}: ${tsType}${trailingChars}`, indentSpaces);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function generateComponentTypesFile(config: ComponentConfig) {
|
|
27
|
-
const properties = config.properties ?? [];
|
|
28
|
-
const eventHandlers = config.events ?? [];
|
|
29
|
-
return `// GENERATED CODE -- DO NOT EDIT!
|
|
30
|
-
//
|
|
31
|
-
// This file is automatically generated by the Superblocks CLI
|
|
32
|
-
// Use the \`superblocks components watch\` command to regenerate this file from its source in config.ts
|
|
33
|
-
|
|
34
|
-
// All properties of your component are defined here.
|
|
35
|
-
// These are the properties which are surfaced in the Superblocks properties panel
|
|
36
|
-
// and can be referenced throughout your Superblocks Application
|
|
37
|
-
export type Props = {
|
|
38
|
-
${properties
|
|
39
|
-
.map((v) => renderInputTypeAsTS(v.path, v.dataType, ";", 2) + "\n")
|
|
40
|
-
.join("")}};
|
|
41
|
-
|
|
42
|
-
export type EventTriggers = {
|
|
43
|
-
// Call the subsequent function(s) to trigger event(s) in Superblocks from your component.
|
|
44
|
-
// These events can be wired up to event handlers in your Superblocks App
|
|
45
|
-
${
|
|
46
|
-
eventHandlers.length === 0
|
|
47
|
-
? " // Note: no event handlers defined\n"
|
|
48
|
-
: eventHandlers
|
|
49
|
-
.map((v: any) => indent(v.path, 2) + ": () => void;" + "\n")
|
|
50
|
-
.join("")
|
|
51
|
-
}};
|
|
52
|
-
`;
|
|
53
|
-
}
|
package/src/identifiers.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// convert a string to upper camel case, e.g. "123 & this is my component, foo-bar" -> "123ThisIsMyComponentFooBar"
|
|
2
|
-
function wordsToUpperCamelCase(str: string) {
|
|
3
|
-
return str
|
|
4
|
-
.split(/[^A-Za-z0-9_]+/)
|
|
5
|
-
.filter(Boolean)
|
|
6
|
-
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
7
|
-
.join("");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const digits = "Zero One Two Three Four Five Six Seven Eight Nine".split(" ");
|
|
11
|
-
|
|
12
|
-
// convert digits to words, e.g. "123" -> "OneTwoThree"
|
|
13
|
-
function digitsToWords(str: string) {
|
|
14
|
-
return str.replace(/\d/g, (char) => digits[parseInt(char, 10)] ?? char);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Suggest an identifier based on a label, e.g. `My Component` -> `myComponent`.
|
|
19
|
-
* @param label The label to suggest an identifier for.
|
|
20
|
-
* @param capitalizeFirst Whether to capitalize the first letter of the identifier.
|
|
21
|
-
*/
|
|
22
|
-
export function suggestIdentifier(label: string, capitalizeFirst = false) {
|
|
23
|
-
const upperCamelCase = wordsToUpperCamelCase(label)
|
|
24
|
-
// convert any digits at the start of the string to words as these are not valid in identifiers
|
|
25
|
-
.replace(/^\d+/, digitsToWords);
|
|
26
|
-
if (capitalizeFirst) {
|
|
27
|
-
return upperCamelCase;
|
|
28
|
-
} else {
|
|
29
|
-
return upperCamelCase[0].toLowerCase() + upperCamelCase.slice(1);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const IdentifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
34
|
-
|
|
35
|
-
export function isValidIdentifier(str: string) {
|
|
36
|
-
return IdentifierRegex.test(str);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const reservedPropertyNames = new Set<string>([
|
|
40
|
-
"ENTITY_TYPE",
|
|
41
|
-
"bindingPaths",
|
|
42
|
-
"bottomRow",
|
|
43
|
-
"dynamicBindingPathList",
|
|
44
|
-
"dynamicTriggerPathList",
|
|
45
|
-
"gridColumns",
|
|
46
|
-
"gridRows",
|
|
47
|
-
"height",
|
|
48
|
-
"isLoading",
|
|
49
|
-
"isVisible",
|
|
50
|
-
"leftColumn",
|
|
51
|
-
"parentId",
|
|
52
|
-
"rightColumn",
|
|
53
|
-
"snapColumns",
|
|
54
|
-
"snapRows",
|
|
55
|
-
"top",
|
|
56
|
-
"topRow",
|
|
57
|
-
"type",
|
|
58
|
-
"widgetId",
|
|
59
|
-
"widgetLastChange",
|
|
60
|
-
"widgetName",
|
|
61
|
-
"width",
|
|
62
|
-
]);
|
|
63
|
-
|
|
64
|
-
export function isReservedPropertyName(str: string) {
|
|
65
|
-
return (
|
|
66
|
-
reservedPropertyNames.has(str) ||
|
|
67
|
-
// also treat any property name starting with "default_" as reserved since we use this prefix for default properties
|
|
68
|
-
str.startsWith("default_")
|
|
69
|
-
);
|
|
70
|
-
}
|
package/src/localGitRepoState.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { ComponentEvent } from "./events.js";
|
|
2
|
-
|
|
3
|
-
export type LocalGitRepoState =
|
|
4
|
-
| { status: "NO_GIT" }
|
|
5
|
-
| { status: "DETACHED" }
|
|
6
|
-
| {
|
|
7
|
-
status: "IN_A_BRANCH";
|
|
8
|
-
localBranchName: string;
|
|
9
|
-
upstream?: {
|
|
10
|
-
remoteName: string;
|
|
11
|
-
url: string;
|
|
12
|
-
pushUrl?: string | null;
|
|
13
|
-
branchName: string;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export interface ValidateGitSetupRequestBody {
|
|
18
|
-
gitState: LocalGitRepoState;
|
|
19
|
-
event?: ComponentEvent;
|
|
20
|
-
}
|
package/src/login.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
|
|
4
|
-
import fs from "fs-extra";
|
|
5
|
-
|
|
6
|
-
import { FileAccessError, TOKEN_CONFIG_PATH } from "./constants.js";
|
|
7
|
-
|
|
8
|
-
type BaseUrl = {
|
|
9
|
-
superblocksBaseUrl: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
type TokenWithBaseUrl = BaseUrl & {
|
|
13
|
-
token: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Returns the resolved auth file path, honoring the SUPERBLOCKS_AUTH_FILE
|
|
18
|
-
* environment variable. Both reads and writes MUST use this so the CLI
|
|
19
|
-
* never writes to one file and reads from another.
|
|
20
|
-
*/
|
|
21
|
-
export function resolveAuthFilePath(): string {
|
|
22
|
-
return (
|
|
23
|
-
process.env.SUPERBLOCKS_AUTH_FILE || join(homedir(), TOKEN_CONFIG_PATH)
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function saveApiToken(superblocksBaseUrl: string, token?: string) {
|
|
28
|
-
try {
|
|
29
|
-
const authFile = resolveAuthFilePath();
|
|
30
|
-
await fs.ensureDir(dirname(authFile));
|
|
31
|
-
if (token) {
|
|
32
|
-
const tokenConfig: TokenWithBaseUrl = {
|
|
33
|
-
superblocksBaseUrl,
|
|
34
|
-
token,
|
|
35
|
-
};
|
|
36
|
-
await fs.writeJSON(authFile, tokenConfig);
|
|
37
|
-
} else {
|
|
38
|
-
const tokenConfig: BaseUrl = {
|
|
39
|
-
superblocksBaseUrl,
|
|
40
|
-
};
|
|
41
|
-
await fs.writeJSON(authFile, tokenConfig);
|
|
42
|
-
}
|
|
43
|
-
} catch {
|
|
44
|
-
throw new FileAccessError("Could not save token");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export async function getLocalTokenWithUrlIfExists(): Promise<
|
|
49
|
-
TokenWithBaseUrl | BaseUrl | undefined
|
|
50
|
-
> {
|
|
51
|
-
try {
|
|
52
|
-
return await getLocalTokenWithUrl();
|
|
53
|
-
} catch {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function getLocalTokenWithUrl(): Promise<
|
|
59
|
-
TokenWithBaseUrl | BaseUrl
|
|
60
|
-
> {
|
|
61
|
-
try {
|
|
62
|
-
const tokenConfig: TokenWithBaseUrl = await fs.readJSON(
|
|
63
|
-
resolveAuthFilePath(),
|
|
64
|
-
);
|
|
65
|
-
const superblocksBaseUrl =
|
|
66
|
-
process.env.SUPERBLOCKS_BASE_URL || tokenConfig.superblocksBaseUrl;
|
|
67
|
-
if (tokenConfig.token) {
|
|
68
|
-
// user logged in
|
|
69
|
-
return {
|
|
70
|
-
token: tokenConfig.token,
|
|
71
|
-
superblocksBaseUrl,
|
|
72
|
-
};
|
|
73
|
-
} else {
|
|
74
|
-
// user not logged in, but set domain
|
|
75
|
-
return {
|
|
76
|
-
superblocksBaseUrl,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
throw new Error("No local API key found");
|
|
81
|
-
}
|
|
82
|
-
}
|
package/src/mask-unix-signals.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const unixSignalsToMask: NodeJS.Signals[] = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
2
|
-
|
|
3
|
-
// The exit code that is used when the process is aborted due to a unix signal
|
|
4
|
-
const exitCode = 254;
|
|
5
|
-
|
|
6
|
-
type ActionResult<T> =
|
|
7
|
-
| { success: true; returnValue: T }
|
|
8
|
-
| { success: false; error: unknown };
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Runs an action with some unix signals being masked. This is useful for actions that should not be interrupted and need
|
|
12
|
-
* to be allowed to complete or perform some cleanup. If a signal is received, the action will be notified that it should
|
|
13
|
-
* abort via the `AbortSignal` that is passed to it. The action may or may not choose to abort immediately. The cleanup
|
|
14
|
-
* action is called after the main action and this happens unconditionally, i.e. regardless of whether a signal was received,
|
|
15
|
-
* the main action threw an error, or the main action returned a value. The cleanup action is passed the abort signal and the
|
|
16
|
-
* error (if any) that was thrown by the main action. If a signal was received, the process will exit after the cleanup
|
|
17
|
-
* action has completed. If the main action threw an error, that error will be thrown again. Otherwise, the main action's
|
|
18
|
-
* return value is returned. This function can be thought of as a (unix) signal aware version of `try { … } finally { … }`
|
|
19
|
-
* and `try { … } catch { … } catch { … }`.
|
|
20
|
-
*
|
|
21
|
-
* *Note*: This function registers listeners for some unix signals, so it affects the global behavior of the process. Be
|
|
22
|
-
* careful when using this function in a library.
|
|
23
|
-
*
|
|
24
|
-
* @param action - The main action to execute.
|
|
25
|
-
* @param cleanup - An optional cleanup action to execute when the process is aborted.
|
|
26
|
-
* @returns The result of the `action`.
|
|
27
|
-
*/
|
|
28
|
-
export async function maskUnixSignals<T>(
|
|
29
|
-
action: (aborted: AbortSignal) => Promise<T>,
|
|
30
|
-
cleanup?: (
|
|
31
|
-
aborted: AbortSignal,
|
|
32
|
-
actionResult: ActionResult<T>,
|
|
33
|
-
) => Promise<void>,
|
|
34
|
-
): Promise<T> {
|
|
35
|
-
const abortController = new AbortController();
|
|
36
|
-
const abortSignal = abortController.signal;
|
|
37
|
-
|
|
38
|
-
const onUnixSignal = (unixSignal: NodeJS.Signals) => {
|
|
39
|
-
console.log(`${unixSignal} received, will abort after cleanup`);
|
|
40
|
-
if (unixSignal === "SIGINT") {
|
|
41
|
-
if (!abortSignal.aborted) {
|
|
42
|
-
console.log("Press Ctrl+C again to force abort");
|
|
43
|
-
} else {
|
|
44
|
-
console.log("Second SIGINT received, will abort immediately");
|
|
45
|
-
process.exit(exitCode);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Pass the signal as the reason for aborting so that `action` and `cleanup` can know which signal was received
|
|
49
|
-
abortController.abort(unixSignal);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
for (const signal of unixSignalsToMask) {
|
|
53
|
-
process.on(signal, onUnixSignal);
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
let result: ActionResult<T>;
|
|
57
|
-
try {
|
|
58
|
-
const returnValue = await action(abortSignal);
|
|
59
|
-
result = { success: true, returnValue };
|
|
60
|
-
} catch (err) {
|
|
61
|
-
result = { success: false, error: err };
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
await cleanup?.(abortSignal, result);
|
|
65
|
-
} finally {
|
|
66
|
-
// If the process received a (unix) signal, this takes precedence over any error that was thrown by `action` or
|
|
67
|
-
// `cleanup`. So we exit here. Otherwise, we let standard JS error handling take over, e.g. an exception
|
|
68
|
-
// thrown by `cleanup` shadows any exception that was thrown by `action`.
|
|
69
|
-
if (abortSignal.aborted) {
|
|
70
|
-
// The process received a (unix) signal, so we exit here.
|
|
71
|
-
process.exit(exitCode);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (!result.success) {
|
|
75
|
-
// `action` threw an error
|
|
76
|
-
throw result.error;
|
|
77
|
-
} else {
|
|
78
|
-
// `action` threw an error
|
|
79
|
-
return result.returnValue;
|
|
80
|
-
}
|
|
81
|
-
} finally {
|
|
82
|
-
for (const signal of unixSignalsToMask) {
|
|
83
|
-
process.off(signal, onUnixSignal);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|