@jchat/addon-sdk 1.0.0-alpha.4 → 1.0.0-alpha.7

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/ui.d.mts ADDED
@@ -0,0 +1 @@
1
+ export * from "@jchat/ui";
package/dist/ui.mjs ADDED
@@ -0,0 +1,2 @@
1
+ export * from "@jchat/ui";
2
+ export {};
package/dist/vite.d.mts CHANGED
@@ -28,6 +28,8 @@ declare function jchatAddon(options: JchatAddonPluginOptions): Plugin;
28
28
  interface JchatAddonPluginOptions {
29
29
  /** The base manifest specification for the addon. */
30
30
  manifest: AddonManifest;
31
+ /** Explicitly declare development mode to permit unsigned database-bearing builds. */
32
+ development?: boolean;
31
33
  /**
32
34
  * Whether to package the build output into a `.zip` archive upon build completion. Defaults to
33
35
  * `true` during production builds and `false` during development builds.
package/dist/vite.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { execFileSync } from "node:child_process";
4
3
  import crypto from "node:crypto";
5
4
  //#region src/vite.ts
6
5
  /**
@@ -33,10 +32,45 @@ function jchatAddon(options) {
33
32
  name: "jchat-addon",
34
33
  apply: "build",
35
34
  config(_, env) {
36
- return { define: { "process.env.NODE_ENV": JSON.stringify(env.mode || "production") } };
35
+ const rootDir = process.cwd();
36
+ const uiEntry = path.resolve(rootDir, "src/ui.tsx");
37
+ const hasUi = fs.existsSync(uiEntry);
38
+ return {
39
+ define: {
40
+ global: "window",
41
+ "process.env.NODE_ENV": JSON.stringify(env.mode || "production")
42
+ },
43
+ build: {
44
+ outDir: "build",
45
+ emptyOutDir: true,
46
+ minify: env.mode === "production",
47
+ ...hasUi ? {
48
+ lib: {
49
+ entry: uiEntry,
50
+ name: manifestData.id,
51
+ formats: ["es"],
52
+ fileName: () => "ui.[hash].js"
53
+ },
54
+ rolldownOptions: {
55
+ external: [
56
+ "react",
57
+ "react-dom",
58
+ "react/jsx-runtime",
59
+ "react/jsx-dev-runtime",
60
+ "jotai",
61
+ "react-hot-toast",
62
+ "@tanstack/react-query",
63
+ "@jchat/addon-sdk",
64
+ "@jchat/addon-sdk/ui"
65
+ ],
66
+ output: { format: "esm" }
67
+ }
68
+ } : {}
69
+ }
70
+ };
37
71
  },
38
72
  configResolved(config) {
39
- developmentBuild = config.mode === "development";
73
+ developmentBuild = options.development ?? (config.mode === "development" || process.env.JCHAT_ADDON_DEV === "true" || process.env.NODE_ENV === "development");
40
74
  if (shouldZip === void 0) shouldZip = config.mode === "production";
41
75
  },
42
76
  async writeBundle() {
@@ -59,9 +93,13 @@ function jchatAddon(options) {
59
93
  }
60
94
  };
61
95
  const serverSrcPath = path.resolve(rootDir, "src/api.ts");
62
- const hasServer = fs.existsSync(serverSrcPath);
96
+ const hasServer = await Bun.file(serverSrcPath).exists();
63
97
  if (hasServer) {
64
- execFileSync("bun", createAddonServerBuildArguments(serverSrcPath, path.resolve(buildDir, "api.js"), developmentBuild), { stdio: "inherit" });
98
+ Bun.spawnSync(["bun", ...createAddonServerBuildArguments(serverSrcPath, path.resolve(buildDir, "api.js"), developmentBuild)], { stdio: [
99
+ "inherit",
100
+ "inherit",
101
+ "inherit"
102
+ ] });
65
103
  manifest = {
66
104
  ...manifest,
67
105
  server: {
@@ -77,12 +115,15 @@ function jchatAddon(options) {
77
115
  });
78
116
  for (const relativePath of databaseFiles) {
79
117
  if (path.isAbsolute(relativePath) || relativePath.includes("\\") || relativePath.split("/").includes("..")) throw new Error(`Unsafe addon database file path: ${relativePath}`);
80
- const source = path.resolve(rootDir, relativePath);
81
- if (!fs.existsSync(source) || !fs.statSync(source).isFile()) throw new Error(`Missing addon database file: ${relativePath}`);
82
118
  const destination = path.resolve(buildDir, relativePath);
83
119
  fs.mkdirSync(path.dirname(destination), { recursive: true });
84
- fs.copyFileSync(source, destination);
85
120
  }
121
+ await Promise.all(databaseFiles.map(async (relativePath) => {
122
+ const source = path.resolve(rootDir, relativePath);
123
+ if (!await Bun.file(source).exists() || !fs.statSync(source).isFile()) throw new Error(`Missing addon database file: ${relativePath}`);
124
+ const destination = path.resolve(buildDir, relativePath);
125
+ return Bun.write(destination, Bun.file(source));
126
+ }));
86
127
  if (manifest.server?.migrations?.length || manifest.server?.seeds?.length) {
87
128
  const signingPrivateKey = requireAddonSigningKey(options.signingPrivateKey ?? process.env.JCHAT_ADDON_SIGNING_PRIVATE_KEY, developmentBuild);
88
129
  if (signingPrivateKey) {
@@ -93,33 +134,39 @@ function jchatAddon(options) {
93
134
  ...manifest.server?.seeds ?? []
94
135
  ];
95
136
  if (hasServer) archivePaths.push("api.js");
96
- manifest = signAddonManifest(manifest, archivePaths.map((file) => ({
137
+ const resolvedFiles = await Promise.all(archivePaths.map(async (file) => ({
97
138
  path: file,
98
- contents: file === "manifest.json" ? Buffer.from("") : fs.readFileSync(path.resolve(buildDir, file))
99
- })), signingPrivateKey);
139
+ contents: file === "manifest.json" ? Buffer.from("") : Buffer.from(await Bun.file(path.resolve(buildDir, file)).arrayBuffer())
140
+ })));
141
+ manifest = signAddonManifest(manifest, resolvedFiles, signingPrivateKey);
100
142
  }
101
143
  }
102
144
  const readmeSource = path.resolve(rootDir, "README.md");
103
- if (fs.existsSync(readmeSource)) fs.copyFileSync(readmeSource, path.resolve(buildDir, "README.md"));
145
+ if (await Bun.file(readmeSource).exists()) await Bun.write(path.resolve(buildDir, "README.md"), Bun.file(readmeSource));
104
146
  const manifestJson = JSON.stringify(manifest, null, 2);
105
- fs.writeFileSync(path.resolve(buildDir, "manifest.json"), manifestJson);
147
+ await Bun.write(path.resolve(buildDir, "manifest.json"), manifestJson);
106
148
  if (shouldZip) {
107
149
  const zipName = `${manifest.id}-${pkg.version}.zip`;
108
150
  const filesToZip = ["manifest.json", bundleFile];
109
- if (fs.existsSync(path.resolve(buildDir, "api.js"))) filesToZip.push("api.js");
110
- if (fs.existsSync(path.resolve(buildDir, "README.md"))) filesToZip.push("README.md");
151
+ if (await Bun.file(path.resolve(buildDir, "api.js")).exists()) filesToZip.push("api.js");
152
+ if (await Bun.file(path.resolve(buildDir, "README.md")).exists()) filesToZip.push("README.md");
111
153
  const migrationsDest = path.resolve(buildDir, "migrations");
112
154
  if (fs.existsSync(migrationsDest)) filesToZip.push("migrations");
113
155
  const seedsDest = path.resolve(buildDir, "seeds");
114
156
  if (fs.existsSync(seedsDest)) filesToZip.push("seeds");
115
- execFileSync("tar", [
157
+ Bun.spawnSync([
158
+ "tar",
116
159
  "-a",
117
160
  "-cf",
118
161
  zipName,
119
162
  ...filesToZip
120
163
  ], {
121
164
  cwd: buildDir,
122
- stdio: "inherit"
165
+ stdio: [
166
+ "inherit",
167
+ "inherit",
168
+ "inherit"
169
+ ]
123
170
  });
124
171
  console.log(`Build Complete: ${manifest.id} v${pkg.version} → ${zipName}`);
125
172
  }
@@ -164,7 +211,7 @@ function signAddonManifest(manifest, files, privateKey) {
164
211
  }
165
212
  };
166
213
  const hash = crypto.createHash("sha256");
167
- for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
214
+ for (const file of [...files].toSorted((a, b) => a.path.localeCompare(b.path))) {
168
215
  hash.update(file.path);
169
216
  hash.update("\0");
170
217
  hash.update(file.path === "manifest.json" ? stableStringify(unsigned) : file.contents);
@@ -225,12 +272,12 @@ function embedCssIntoBundle(buildDir, addonId) {
225
272
  fs.unlinkSync(cssPath);
226
273
  } catch {}
227
274
  }
275
+ function sortValue(child) {
276
+ if (Array.isArray(child)) return child.map(sortValue);
277
+ if (child && typeof child === "object") return Object.fromEntries(Object.entries(child).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
278
+ return child;
279
+ }
228
280
  function stableStringify(value) {
229
- const sortValue = (child) => {
230
- if (Array.isArray(child)) return child.map(sortValue);
231
- if (child && typeof child === "object") return Object.fromEntries(Object.entries(child).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
232
- return child;
233
- };
234
281
  return JSON.stringify(sortValue(value));
235
282
  }
236
283
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@jchat/addon-sdk",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.7",
4
4
  "private": false,
5
+ "description": "JChat Addon SDK for building JChat addons.",
5
6
  "keywords": [
6
7
  "addon",
7
8
  "chat",
@@ -11,14 +12,18 @@
11
12
  "sdk",
12
13
  "vite"
13
14
  ],
14
- "license": "Apache-2.0",
15
+ "license": "SEE LICENSE IN LICENSE",
15
16
  "repository": {
16
17
  "type": "git",
17
18
  "url": "git+https://github.com/mjjabarullah/jchat.git",
18
19
  "directory": "packages/addon-sdk"
19
20
  },
21
+ "bin": {
22
+ "jchat-addon": "./dist/cli.mjs"
23
+ },
20
24
  "files": [
21
25
  "dist",
26
+ "tsconfig.addon.json",
22
27
  "README.md",
23
28
  "CHANGELOG.md"
24
29
  ],
@@ -30,6 +35,10 @@
30
35
  "types": "./dist/index.d.mts",
31
36
  "import": "./dist/index.mjs"
32
37
  },
38
+ "./cli": {
39
+ "types": "./dist/cli.d.mts",
40
+ "import": "./dist/cli.mjs"
41
+ },
33
42
  "./diagnostics": {
34
43
  "types": "./dist/diagnostics.d.mts",
35
44
  "import": "./dist/diagnostics.mjs"
@@ -37,7 +46,17 @@
37
46
  "./vite": {
38
47
  "types": "./dist/vite.d.mts",
39
48
  "import": "./dist/vite.mjs"
40
- }
49
+ },
50
+ "./ui": {
51
+ "types": "./dist/ui.d.mts",
52
+ "import": "./dist/ui.mjs"
53
+ },
54
+ "./ui/tailwind.css": {
55
+ "style": "./src/tailwind.css",
56
+ "import": "./src/tailwind.css",
57
+ "default": "./src/tailwind.css"
58
+ },
59
+ "./tsconfig.json": "./tsconfig.addon.json"
41
60
  },
42
61
  "publishConfig": {
43
62
  "access": "public"
@@ -49,8 +68,13 @@
49
68
  "typecheck": "tsc --noEmit",
50
69
  "prepack": "bun run build"
51
70
  },
71
+ "dependencies": {},
52
72
  "devDependencies": {
53
73
  "@jchat/icon": "workspace:*",
74
+ "@jchat/ui": "workspace:*",
75
+ "@tailwindcss/vite": "catalog:",
76
+ "@tanstack/react-query": "catalog:",
77
+ "@types/bun": "catalog:",
54
78
  "@types/react": "catalog:",
55
79
  "elysia": "catalog:",
56
80
  "kysely": "^catalog:",
@@ -58,6 +82,8 @@
58
82
  "vite": "catalog:"
59
83
  },
60
84
  "peerDependencies": {
85
+ "@tailwindcss/vite": "catalog:",
86
+ "@tanstack/react-query": "catalog:",
61
87
  "elysia": "catalog:",
62
88
  "kysely": "^catalog:",
63
89
  "react": "catalog:",
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "JChat Addon Base",
4
+ "compilerOptions": {
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleResolution": "bundler",
8
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
9
+ "jsx": "react-jsx",
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "verbatimModuleSyntax": true,
13
+ "resolveJsonModule": true,
14
+ "skipLibCheck": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "types": ["bun", "vite/client"]
18
+ },
19
+ "exclude": ["build", "dist", "node_modules"]
20
+ }