@examplary/cli 1.15.0 → 1.17.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.
Files changed (43) hide show
  1. package/README.md +21 -24
  2. package/dist/commands/init.d.ts +14 -0
  3. package/dist/commands/init.js +103 -0
  4. package/dist/commands/package.d.ts +1 -0
  5. package/dist/commands/package.js +16 -0
  6. package/dist/commands/publish.d.ts +5 -0
  7. package/dist/commands/publish.js +57 -0
  8. package/dist/commands/set-secret.d.ts +6 -0
  9. package/dist/commands/set-secret.js +29 -0
  10. package/dist/commands/validate.d.ts +0 -1
  11. package/dist/commands/validate.js +2 -20
  12. package/dist/index.js +38 -29
  13. package/dist/lib/api.d.ts +1 -1
  14. package/dist/lib/api.js +17 -6
  15. package/dist/lib/build-app.d.ts +21 -0
  16. package/dist/lib/build-app.js +179 -0
  17. package/dist/lib/build-current-app.d.ts +4 -0
  18. package/dist/lib/build-current-app.js +21 -0
  19. package/dist/lib/build-question-type.d.ts +8 -0
  20. package/dist/lib/build-question-type.js +48 -0
  21. package/dist/lib/bundle.d.ts +4 -3
  22. package/dist/lib/bundle.js +9 -31
  23. package/dist/lib/styles.d.ts +2 -1
  24. package/dist/lib/styles.js +3 -1
  25. package/dist/lib/validate.d.ts +2 -4
  26. package/dist/lib/validate.js +7 -26
  27. package/package.json +4 -24
  28. package/dist/commands/preview.d.ts +0 -5
  29. package/dist/commands/preview.js +0 -195
  30. package/dist/commands/upload.d.ts +0 -5
  31. package/dist/commands/upload.js +0 -125
  32. package/dist/lib/read-meta.d.ts +0 -20
  33. package/dist/lib/read-meta.js +0 -44
  34. package/dist/lib/safe-path.d.ts +0 -5
  35. package/dist/lib/safe-path.js +0 -13
  36. package/dist/preview-ui/assets/icon-nodes-CxaTPv-R.js +0 -2
  37. package/dist/preview-ui/assets/icon-nodes-CxaTPv-R.js.map +0 -1
  38. package/dist/preview-ui/assets/index-b38eDKFI.js +0 -3663
  39. package/dist/preview-ui/assets/index-b38eDKFI.js.map +0 -1
  40. package/dist/preview-ui/assets/index-boullcXq.css +0 -1
  41. package/dist/preview-ui/assets/tags-BUvo71XG.js +0 -2
  42. package/dist/preview-ui/assets/tags-BUvo71XG.js.map +0 -1
  43. package/dist/preview-ui/index.html +0 -21
@@ -0,0 +1,179 @@
1
+ import { createHash } from "crypto";
2
+ import { access, mkdir, readFile } from "fs/promises";
3
+ import { tmpdir } from "os";
4
+ import { basename, dirname, extname, join, resolve } from "path";
5
+ import { pathToFileURL } from "url";
6
+ import { AppSchema } from "@examplary/schemas";
7
+ import { build } from "esbuild";
8
+ import { buildQuestionType } from "./build-question-type.js";
9
+ import { buildComponent, splitInlineSourcemap } from "./bundle.js";
10
+ import { buildStyles } from "./styles.js";
11
+ const CONTENT_TYPES = {
12
+ ".svg": "image/svg+xml",
13
+ ".png": "image/png",
14
+ ".jpg": "image/jpeg",
15
+ ".jpeg": "image/jpeg",
16
+ ".webp": "image/webp",
17
+ ".gif": "image/gif",
18
+ ".md": "text/markdown",
19
+ ".json": "application/json",
20
+ };
21
+ const APP_FILES = ["app.ts", "app.js", "app.mjs"];
22
+ const sha256 = (contents) => createHash("sha256").update(contents).digest("hex");
23
+ export const findAppFile = async (dir) => {
24
+ for (const name of APP_FILES) {
25
+ const file = resolve(dir, name);
26
+ try {
27
+ await access(file);
28
+ return file;
29
+ }
30
+ catch {
31
+ continue;
32
+ }
33
+ }
34
+ throw new Error(`No ${APP_FILES.join(", ")} found in ${dir}`);
35
+ };
36
+ /** Evaluates the app module in Node, returning the definition `defineApp()` built. */
37
+ export const loadAppDefinition = async (file) => {
38
+ const outdir = join(tmpdir(), "examplary-cli");
39
+ await mkdir(outdir, { recursive: true });
40
+ const outfile = join(outdir, `${sha256(file).slice(0, 12)}.mjs`);
41
+ await build({
42
+ entryPoints: [file],
43
+ bundle: true,
44
+ platform: "node",
45
+ format: "esm",
46
+ target: "node22",
47
+ outfile,
48
+ banner: {
49
+ js: `import { createRequire } from "module"; const require = createRequire(import.meta.url);`,
50
+ },
51
+ logLevel: "silent",
52
+ });
53
+ const module = await import(`${pathToFileURL(outfile).href}?t=${Date.now()}`);
54
+ if (typeof module.default?.toDefinition !== "function") {
55
+ throw new Error(`${file} has to default-export defineApp()`);
56
+ }
57
+ return module.default.toDefinition();
58
+ };
59
+ /**
60
+ * Bundles the app for the sandbox, which only offers standard JavaScript and
61
+ * `fetch`: a script defining `examplaryApp`, whose default export runs a hook.
62
+ */
63
+ export const buildServerBundle = async (file) => {
64
+ const result = await build({
65
+ stdin: {
66
+ contents: `import app from ${JSON.stringify(file)};
67
+ export default (input) => app.runHook(input);`,
68
+ resolveDir: dirname(file),
69
+ },
70
+ bundle: true,
71
+ platform: "neutral",
72
+ mainFields: ["module", "main"],
73
+ format: "iife",
74
+ globalName: "examplaryApp",
75
+ target: "es2020",
76
+ minify: true,
77
+ sourcemap: "inline",
78
+ write: false,
79
+ logLevel: "silent",
80
+ });
81
+ return splitInlineSourcemap(result.outputFiles[0].text);
82
+ };
83
+ /** Uploads a file the app refers to by a relative path; URLs pass through. */
84
+ const uploadPath = async (dir, path, upload) => {
85
+ if (/^https?:\/\//.test(path))
86
+ return path;
87
+ const contents = await readFile(resolve(dir, path));
88
+ const contentType = CONTENT_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
89
+ return upload(basename(path), contents, contentType);
90
+ };
91
+ /** Like `uploadPath`, for a path or an object of paths by language. */
92
+ export const uploadTranslatablePath = async (dir, value, upload) => {
93
+ if (value === undefined)
94
+ return undefined;
95
+ if (typeof value === "string")
96
+ return uploadPath(dir, value, upload);
97
+ const entries = await Promise.all(Object.entries(value).map(async ([language, path]) => [
98
+ language,
99
+ await uploadPath(dir, path, upload),
100
+ ]));
101
+ return Object.fromEntries(entries);
102
+ };
103
+ /**
104
+ * Uploads a bundle's source map, then the bundle pointing at it, for readable
105
+ * reviews and errors. Its digest pins the reviewed code: whatever runs it checks it.
106
+ */
107
+ const uploadBundle = async (name, { js, sourcemap }, upload) => {
108
+ const sourcemapUrl = sourcemap && (await upload(`${name}.map`, sourcemap, "application/json"));
109
+ const contents = sourcemapUrl
110
+ ? `${js}\n//# sourceMappingURL=${sourcemapUrl}`
111
+ : js;
112
+ return {
113
+ url: await upload(name, contents, "application/javascript"),
114
+ sha256: sha256(contents),
115
+ };
116
+ };
117
+ const uploadComponent = async (dir, { id, path, translations }, upload) => {
118
+ const bundle = await buildComponent(resolve(dir, path));
119
+ return {
120
+ id,
121
+ ...(await uploadBundle(`${id}.js`, bundle, upload)),
122
+ translations,
123
+ };
124
+ };
125
+ /** The Tailwind classes the app's components use, which the dashboard loads with them. */
126
+ const uploadStyles = async (dir, upload) => {
127
+ const css = await buildStyles(dir);
128
+ return css ? upload("styles.css", css, "text/css") : undefined;
129
+ };
130
+ const uploadAsset = async (dir, { id, path }, upload) => ({
131
+ id,
132
+ url: await uploadPath(dir, path, upload),
133
+ sha256: sha256(await readFile(resolve(dir, path))),
134
+ });
135
+ /**
136
+ * Turns the app in `file` into the manifest `POST /apps` accepts: files
137
+ * uploaded, components and hooks bundled, and hooks listed by ID. Throws when
138
+ * the result isn't a valid app.
139
+ */
140
+ export const buildAppManifest = async (file, definition, uploadFile) => {
141
+ const { hooks, components, assets, questionTypes, ...app } = definition;
142
+ const dir = dirname(file);
143
+ // What the app consists of, without the uploads' fresh URLs, so the API can
144
+ // tell an unchanged upload apart
145
+ const digests = [];
146
+ const upload = (fileName, contents, contentType) => {
147
+ const withoutUrls = typeof contents === "string"
148
+ ? contents.replace(/\n\/\/# sourceMappingURL=\S+$/, "")
149
+ : contents;
150
+ digests.push(`${fileName}:${sha256(withoutUrls)}`);
151
+ return uploadFile(fileName, contents, contentType);
152
+ };
153
+ const server = hooks.length &&
154
+ (await uploadBundle("server.js", await buildServerBundle(file), upload));
155
+ const manifest = {
156
+ ...app,
157
+ icon: await uploadTranslatablePath(dir, app.icon, upload),
158
+ readme: await uploadTranslatablePath(dir, app.readme, upload),
159
+ previewImage: await uploadTranslatablePath(dir, app.previewImage, upload),
160
+ hooks: hooks.map(({ id }) => ({ id })),
161
+ server: server ? server.url : undefined,
162
+ serverSha256: server ? server.sha256 : undefined,
163
+ components: await Promise.all(components.map((component) => uploadComponent(dir, component, upload))),
164
+ stylesheet: components.length ? await uploadStyles(dir, upload) : undefined,
165
+ assets: await Promise.all(assets.map((asset) => uploadAsset(dir, asset, upload))),
166
+ questionTypes: [],
167
+ };
168
+ // One after the other, as each bundles a few components and runs its test cases
169
+ for (const questionType of questionTypes ?? []) {
170
+ manifest.questionTypes.push(await buildQuestionType(questionType, dir, upload));
171
+ }
172
+ manifest.contentSha256 = sha256([JSON.stringify(definition), ...digests.sort()].join("\n"));
173
+ const parsed = AppSchema.safeParse(manifest);
174
+ if (!parsed.success) {
175
+ const issues = parsed.error.issues.map((issue) => `${issue.path.join(".") || "app"}: ${issue.message}`);
176
+ throw new Error(`Invalid app definition:\n${issues.join("\n")}`);
177
+ }
178
+ return manifest;
179
+ };
@@ -0,0 +1,4 @@
1
+ import { Uploader } from "./build-app.js";
2
+ export declare const dryUpload: Uploader;
3
+ /** Builds the app in the current directory, exiting on any error so a partial app is never published. */
4
+ export declare const buildCurrentApp: (upload: Uploader, quiet?: boolean) => Promise<Record<string, any>>;
@@ -0,0 +1,21 @@
1
+ import { cwd, exit } from "process";
2
+ import { buildAppManifest, findAppFile, loadAppDefinition, } from "./build-app.js";
3
+ export const dryUpload = async (fileName) => `https://validate.invalid/${fileName}`;
4
+ /** Builds the app in the current directory, exiting on any error so a partial app is never published. */
5
+ export const buildCurrentApp = async (upload, quiet = false) => {
6
+ try {
7
+ const file = await findAppFile(cwd());
8
+ const definition = await loadAppDefinition(file);
9
+ if (!quiet)
10
+ console.log(`Building app: ${definition.id}...`);
11
+ const manifest = await buildAppManifest(file, definition, upload);
12
+ if (!quiet) {
13
+ console.log(`• Bundled ${manifest.components.length} component(s), ${manifest.hooks.length} hook(s) and ${manifest.questionTypes.length} question type(s)`);
14
+ }
15
+ return manifest;
16
+ }
17
+ catch (e) {
18
+ console.error(`🚫 ${e.message}`);
19
+ exit(1);
20
+ }
21
+ };
@@ -0,0 +1,8 @@
1
+ import type { Uploader } from "./build-app.js";
2
+ /**
3
+ * Checks a question type of an app (incl. its response processing test cases),
4
+ * bundles its components, and uploads them with its icon, README
5
+ * and preview image, all by paths relative to the app's directory `dir`.
6
+ * Returns the definition `POST /apps` takes.
7
+ */
8
+ export declare const buildQuestionType: (questionType: Record<string, any>, dir: string, uploadFile: Uploader) => Promise<Record<string, any>>;
@@ -0,0 +1,48 @@
1
+ import { uploadTranslatablePath } from "./build-app.js";
2
+ import { isValid, printValidationResult, validateDefinition, } from "./validate.js";
3
+ /** Uploads a bundle, with its source map next to it. */
4
+ const uploadBundle = async (name, { js, sourcemap }, upload) => {
5
+ const sourcemapUrl = sourcemap && (await upload(`${name}.map`, sourcemap, "application/json"));
6
+ return upload(name, sourcemapUrl ? `${js}\n//# sourceMappingURL=${sourcemapUrl}` : js, "application/javascript");
7
+ };
8
+ /** Uploaded files by language, e.g. a `readme` given as one path or one per language. */
9
+ const uploadByLanguage = async (dir, paths, upload) => {
10
+ const urls = await uploadTranslatablePath(dir, paths, upload);
11
+ return typeof urls === "string" ? { en: urls } : urls;
12
+ };
13
+ /**
14
+ * Checks a question type of an app (incl. its response processing test cases),
15
+ * bundles its components, and uploads them with its icon, README
16
+ * and preview image, all by paths relative to the app's directory `dir`.
17
+ * Returns the definition `POST /apps` takes.
18
+ */
19
+ export const buildQuestionType = async (questionType, dir, uploadFile) => {
20
+ // A copy: the app's own definition is hashed without the upload URLs
21
+ const { readme, previewImage, ...definition } = structuredClone(questionType);
22
+ // An app has several question types, all with files like `component-print.js`
23
+ const prefix = definition.id?.split(".").pop();
24
+ const upload = (name, contents, contentType) => uploadFile(`${prefix}.${name}`, contents, contentType);
25
+ const result = await validateDefinition(definition, dir);
26
+ if (!isValid(result)) {
27
+ printValidationResult(result);
28
+ throw new Error(`Question type ${definition.id} isn't valid`);
29
+ }
30
+ const components = {};
31
+ for (const [type, output] of Object.entries(result.bundles)) {
32
+ components[type] = await uploadBundle(`component-${type}.js`, output, upload);
33
+ }
34
+ definition.components = components;
35
+ if (result.styles) {
36
+ definition.stylesheet = await upload("styles.css", result.styles, "text/css");
37
+ }
38
+ if (definition.icon && result.iconContents) {
39
+ definition.icon = await upload("icon.svg", result.iconContents, "image/svg+xml");
40
+ }
41
+ definition.readmeUrls = await uploadByLanguage(dir, readme, upload);
42
+ definition.readme = definition.readmeUrls?.en;
43
+ definition.previewImageUrls = await uploadByLanguage(dir, previewImage, upload);
44
+ definition.previewImage = definition.previewImageUrls?.en;
45
+ // Test cases have run during validation, they don't need storing
46
+ delete definition.responseProcessing?.testCases;
47
+ return definition;
48
+ };
@@ -1,9 +1,10 @@
1
- export declare const buildComponent: (file: string) => Promise<{
1
+ /** A bundle built with an inline source map, as the code and the map apart. */
2
+ export declare const splitInlineSourcemap: (output: string) => {
2
3
  js: string;
3
4
  sourcemap: string;
4
5
  sha256: string;
5
- }>;
6
- export declare const buildFunction: (file: string) => Promise<{
6
+ };
7
+ export declare const buildComponent: (file: string) => Promise<{
7
8
  js: string;
8
9
  sourcemap: string;
9
10
  sha256: string;
@@ -3,6 +3,14 @@ import { build } from "esbuild";
3
3
  const calculateHash = (compiled) => {
4
4
  return createHash("sha256").update(compiled).digest("hex");
5
5
  };
6
+ /** A bundle built with an inline source map, as the code and the map apart. */
7
+ export const splitInlineSourcemap = (output) => {
8
+ const [js, encoded] = output.split("\n//# sourceMappingURL=data:application/json;base64,");
9
+ const sourcemap = encoded
10
+ ? Buffer.from(encoded.trim(), "base64").toString("utf-8")
11
+ : "";
12
+ return { js: js.trim(), sourcemap, sha256: calculateHash(js.trim()) };
13
+ };
6
14
  export const buildComponent = async (file) => {
7
15
  const res = await build({
8
16
  entryPoints: [file],
@@ -20,35 +28,5 @@ export const buildComponent = async (file) => {
20
28
  "react-dom/client",
21
29
  ],
22
30
  });
23
- let js = res.outputFiles[0].text;
24
- let sourcemap = "";
25
- const sourceMapStatement = "\n//# sourceMappingURL=data:application/json;base64,";
26
- if (js.includes(sourceMapStatement)) {
27
- sourcemap = Buffer.from(js.split(sourceMapStatement)[1].trim(), "base64").toString("utf-8");
28
- js = js.split(sourceMapStatement)[0].trim();
29
- }
30
- const sha256 = calculateHash(js);
31
- return { js, sourcemap, sha256 };
32
- };
33
- export const buildFunction = async (file) => {
34
- const res = await build({
35
- entryPoints: [file],
36
- bundle: true,
37
- write: false,
38
- minify: true,
39
- sourcemap: "inline",
40
- platform: "neutral",
41
- mainFields: ["module", "main"],
42
- format: "iife",
43
- globalName: "userFunction",
44
- });
45
- let js = res.outputFiles[0].text;
46
- let sourcemap = "";
47
- const sourceMapStatement = "\n//# sourceMappingURL=data:application/json;base64,";
48
- if (js.includes(sourceMapStatement)) {
49
- sourcemap = Buffer.from(js.split(sourceMapStatement)[1].trim(), "base64").toString("utf-8");
50
- js = js.split(sourceMapStatement)[0].trim();
51
- }
52
- const sha256 = calculateHash(js);
53
- return { js, sourcemap, sha256 };
31
+ return splitInlineSourcemap(res.outputFiles[0].text);
54
32
  };
@@ -1 +1,2 @@
1
- export declare const buildStyles: () => Promise<string | null>;
1
+ /** The Tailwind classes used by the files in `base`, or null when there are none. */
2
+ export declare const buildStyles: (base?: string) => Promise<string | null>;
@@ -4,7 +4,8 @@ import tailwindcss from "@tailwindcss/postcss";
4
4
  import postcss from "postcss";
5
5
  const require = createRequire(import.meta.url);
6
6
  const tailwindDir = path.dirname(require.resolve("tailwindcss/package.json"));
7
- export const buildStyles = async () => {
7
+ /** The Tailwind classes used by the files in `base`, or null when there are none. */
8
+ export const buildStyles = async (base = process.cwd()) => {
8
9
  const absolutePath = path.resolve(tailwindDir);
9
10
  const inputCSS = `
10
11
  @import "${absolutePath}/theme.css" layer(theme);
@@ -12,6 +13,7 @@ export const buildStyles = async () => {
12
13
  `;
13
14
  const result = await postcss([
14
15
  tailwindcss({
16
+ base,
15
17
  optimize: { minify: true },
16
18
  }),
17
19
  ]).process(inputCSS, { from: "module-css.css" });
@@ -1,4 +1,4 @@
1
- import type { QuestionTypeDefinition } from "./read-meta.js";
1
+ import type { QuestionType } from "@examplary/schemas";
2
2
  import { TestCaseResult } from "./response-processing.js";
3
3
  export type BuiltComponent = {
4
4
  js: string;
@@ -18,8 +18,6 @@ export type ValidationResult = {
18
18
  styles?: string;
19
19
  /** Raw icon file contents, present only when an icon is configured. */
20
20
  iconContents?: string;
21
- /** Bundled function output, keyed by function name, ready to upload. */
22
- functions: Record<string, BuiltComponent>;
23
21
  };
24
22
  /**
25
23
  * Runs every check the `upload` command performs, short of actually uploading:
@@ -27,7 +25,7 @@ export type ValidationResult = {
27
25
  * response processing test cases. Bundled artifacts are returned so callers can
28
26
  * reuse them for an upload without bundling twice.
29
27
  */
30
- export declare const validateDefinition: (definition: QuestionTypeDefinition, dir: string) => Promise<ValidationResult>;
28
+ export declare const validateDefinition: (definition: Partial<QuestionType>, dir: string) => Promise<ValidationResult>;
31
29
  /** Whether the definition is safe to upload. */
32
30
  export declare const isValid: (result: ValidationResult) => boolean;
33
31
  /** Prints warnings, errors, and a test-case summary to the console. */
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "fs/promises";
2
- import { resolve } from "path";
3
- import { buildComponent, buildFunction } from "./bundle.js";
2
+ import { dirname, resolve } from "path";
3
+ import { buildComponent } from "./bundle.js";
4
4
  import { deepCompileJsonata } from "./jsonata.js";
5
5
  import { runResponseProcessingTestCases, } from "./response-processing.js";
6
6
  import { buildStyles } from "./styles.js";
@@ -20,7 +20,6 @@ export const validateDefinition = async (definition, dir) => {
20
20
  const errors = [];
21
21
  const warnings = [];
22
22
  const bundles = {};
23
- const functions = {};
24
23
  let styles;
25
24
  let iconContents;
26
25
  if (!definition.id)
@@ -90,24 +89,11 @@ export const validateDefinition = async (definition, dir) => {
90
89
  errors.push(`Error bundling component ${componentType} from ${componentPath}: ${error.message}`);
91
90
  }
92
91
  }
93
- // Bundle functions to confirm they build and to capture their output
94
- for (const [functionName, functionPath] of Object.entries(definition.functions || {})) {
95
- const realPath = resolve(dir, functionPath);
96
- try {
97
- const output = await buildFunction(realPath);
98
- functions[functionName] = output;
99
- const sizeInKb = output.js.length / 1000;
100
- if (sizeInKb > 100) {
101
- errors.push(`Function ${functionName} is larger than the 100 kB limit (${sizeInKb.toFixed(2)} kB)`);
102
- }
103
- }
104
- catch (error) {
105
- errors.push(`Error bundling function ${functionName} from ${functionPath}: ${error.message}`);
106
- }
107
- }
108
- // Build styles if any components are present
109
- if (Object.keys(bundles).length) {
110
- styles = (await buildStyles()) ?? undefined;
92
+ // The Tailwind classes used in the folder of its components
93
+ const [componentPath] = Object.values(definition.components || {});
94
+ if (Object.keys(bundles).length && componentPath) {
95
+ styles =
96
+ (await buildStyles(dirname(resolve(dir, componentPath)))) ?? undefined;
111
97
  }
112
98
  // Validate the icon file
113
99
  if (definition.icon) {
@@ -127,7 +113,6 @@ export const validateDefinition = async (definition, dir) => {
127
113
  warnings,
128
114
  testCaseResults,
129
115
  bundles,
130
- functions,
131
116
  styles,
132
117
  iconContents,
133
118
  };
@@ -140,10 +125,6 @@ export const printValidationResult = (result) => {
140
125
  const sizeInKb = (output.js.length / 1000).toFixed(2);
141
126
  console.log(`• Bundled component ${componentType} (${sizeInKb} kB)`);
142
127
  }
143
- for (const [functionName, output] of Object.entries(result.functions)) {
144
- const sizeInKb = (output.js.length / 1000).toFixed(2);
145
- console.log(`• Bundled function ${functionName} (${sizeInKb} kB)`);
146
- }
147
128
  for (const warning of result.warnings) {
148
129
  console.warn(` ⚠️ ${warning.message}`);
149
130
  if (warning.file && process.env.GITHUB_ACTIONS) {
package/package.json CHANGED
@@ -1,17 +1,15 @@
1
1
  {
2
2
  "name": "@examplary/cli",
3
- "description": "Command-line tools for building and publishing Examplary question types.",
3
+ "description": "Command-line tools for building and publishing Examplary apps.",
4
4
  "packageManager": "yarn@4.8.1",
5
- "version": "1.15.0",
5
+ "version": "1.17.0",
6
6
  "bin": {
7
7
  "exp": "./dist/index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "release": "semantic-release -e semantic-release-monorepo",
11
- "build:preview-ui": "vite build",
12
11
  "build:js": "tsc --project tsconfig.json",
13
- "build": "yarn build:js && yarn build:preview-ui",
14
- "dev": "tsx scripts/dev.ts",
12
+ "build": "yarn build:js",
15
13
  "test": "vitest run"
16
14
  },
17
15
  "publishConfig": {
@@ -22,36 +20,18 @@
22
20
  ],
23
21
  "dependencies": {
24
22
  "@examplary/schemas": "*",
25
- "@examplary/ui": "*",
26
- "@hono/node-server": "^2.1.3",
27
23
  "@tailwindcss/postcss": "^4.3.3",
28
- "@tailwindcss/vite": "^4.3.0",
29
24
  "esbuild": "^0.28.1",
30
- "hono": "^4.13.11",
31
25
  "jsonata": "^2.2.0",
32
- "lucide-react": "^1.39.0",
33
- "open": "^11.0.0",
26
+ "postcss": "^8.5.28",
34
27
  "tailwindcss": "^4.2.1",
35
- "ws": "^8.22.0",
36
- "yaml": "^2.8.3",
37
28
  "yargs": "^18.0.0"
38
29
  },
39
30
  "devDependencies": {
40
31
  "@types/node": "^24.1.0",
41
32
  "@types/yargs": "^17.0.35",
42
- "@vitejs/plugin-react": "^6.0.1",
43
- "i18next": "^26.3.4",
44
- "i18next-browser-languagedetector": "^8.2.0",
45
- "postcss": "^8.5.28",
46
- "react": "^19.2.8",
47
- "react-dom": "^19.2.8",
48
- "react-error-boundary": "^6.1.6",
49
- "react-i18next": "^17.0.8",
50
- "swr": "^2.3.6",
51
33
  "tsx": "^4.23.15",
52
34
  "typescript": "^7.0.2",
53
- "use-local-storage": "^3.0.0",
54
- "vite": "^8.3.4",
55
35
  "vitest": "^5.0.3"
56
36
  },
57
37
  "homepage": "https://examplary.ai/developers",
@@ -1,5 +0,0 @@
1
- #! /usr/bin/env node
2
- export declare const previewCommand: (argv: {
3
- host: string;
4
- key: string;
5
- }) => Promise<void>;