@jchat/create-addon 1.0.0-alpha.10

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/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # @jchat/create-addon
2
+
3
+ ## 1.0.0-alpha.10
4
+
5
+ ## 1.0.0-alpha.9
6
+
7
+ ## 1.0.0-alpha.8
8
+
9
+ ## 1.0.0-alpha.7
10
+
11
+ ### Minor Changes
12
+
13
+ - 7cef24c: - Enhance addon package diagnostics with semantic version compatibility validation for JChat host and Addon SDK
14
+ - Add support for local workspace development, sandbox test harnesses, and distribution packaging
15
+ - Improve runtime license verification and multi-environment path resolution
16
+ - Update addon generator templates with 20-language i18n support, standard scripts, and type definitions
17
+
18
+ ## 1.0.0-alpha.6
19
+
20
+ ## 1.0.0-alpha.5
21
+
22
+ ### Patch Changes
23
+
24
+ - 9755c28: Export curated UI components, types, and Tailwind styles from `@jchat/addon-sdk/ui` (`@jchat/addon-sdk/ui/tailwind.css`) for sandbox and third-party addon developers.
25
+ - 8b8297f: Rename package from `create-jchat-addon` to `@jchat/create-addon` and update binary script to `create-addon`.
26
+ - 5bbb2b6: Inherit jchatVersion and addonSdkVersion directly from @jchat/create-addon package version when generating standalone devkit.
27
+
28
+ ## 1.0.0-alpha.4
29
+
30
+ ### Patch Changes
31
+
32
+ - 8b8297f: Rename package from `create-jchat-addon` to `@jchat/create-addon` and update binary script to `create-addon`.
33
+ - 5bbb2b6: Inherit jchatVersion and addonSdkVersion directly from @jchat/create-addon package version when generating standalone devkit.
34
+
35
+ ## 1.0.0-alpha.3
36
+
37
+ ## 1.0.0-alpha.2
38
+
39
+ ## 1.0.0-alpha.1
40
+
41
+ ### Patch Changes
42
+
43
+ - 70537c0: Cleanup folder structure.
44
+
45
+ ## 1.0.0-alpha.0
46
+
47
+ - Initial public alpha release.
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ CUSTOM COMMERCIAL END USER LICENSE AGREEMENT (EULA)
2
+
3
+ Copyright (c) 2026 JChat Project. All rights reserved.
4
+
5
+ 1. DEFINITIONS
6
+ "Software" refers to this npm package and its source code.
7
+ "Authorized Application" refers strictly to the application known as "JChat".
8
+
9
+ 2. LICENSE GRANT
10
+ Subject to the terms of this Agreement and payment of any applicable fees, the
11
+ Licensor grants you a non-exclusive, non-transferable, revocable license to
12
+ download, install, and use the Software.
13
+
14
+ 3. RESTRICTIONS ON USE
15
+ You are expressly prohibited from using this Software, in whole or in part,
16
+ in any application, product, or service other than the Authorized Application (JChat).
17
+ Any deployment, linking, execution, or inclusion of this Software into an
18
+ application that is not JChat constitutes a breach of this contract and
19
+ an infringement of copyright law.
20
+
21
+ 4. NO REDISTRIBUTION
22
+ You may not sub-license, rent, lease, or publicly redistribute this Software
23
+ to any third party without explicit written consent from the Licensor.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # @jchat/create-addon
2
+
3
+ A CLI tool for generating a self-contained JChat addon development workspace.
4
+
5
+ ## Usage
6
+
7
+ Generate a new addon workspace:
8
+
9
+ ```bash
10
+ bunx @jchat/create-addon <addon-id>
11
+ ```
12
+
13
+ ### Options
14
+
15
+ - `<addon-id>`: (Required) The unique ID for your addon, typically hyphenated (e.g., `weather-addon`).
16
+ - `--target=<directory>`: (Optional) The directory to generate the workspace in. Defaults to `../<addon-id>` relative to the current working directory.
17
+ - `--image=<image>`: (Optional) A specific Docker / Podman container image to use for the sandbox environment. Defaults to `ghcr.io/mjjabarullah/jchat-sandbox`.
18
+
19
+ ### Example
20
+
21
+ ```bash
22
+ bunx @jchat/create-addon my-addon
23
+ cd my-addon
24
+ ```
25
+
26
+ ## Requirements
27
+
28
+ - Bun 1.3+
29
+ - Docker or Podman
30
+
31
+ ## Licensing
32
+
33
+ The creator package and JChat-authored template material are licensed under Apache License 2.0.
34
+ You retain ownership of the original code and content you add to a generated project and may choose
35
+ the license for that original work. Any JChat-authored template portions and third-party components
36
+ remain subject to their respective licenses and notices.
37
+
38
+ The generated developer sandbox does not grant a production JChat license. Production use remains
39
+ subject to the applicable JChat and addon marketplace terms.
package/dist/cli.d.mts ADDED
@@ -0,0 +1,11 @@
1
+ //#region src/cli.d.ts
2
+ interface CreatorArguments {
3
+ addonId: string;
4
+ targetDir: string;
5
+ sandboxImage?: string;
6
+ workspace?: boolean;
7
+ }
8
+ declare function parseCreatorArguments(arguments_: readonly string[]): CreatorArguments;
9
+ declare function runCreator(arguments_: readonly string[]): Promise<string>;
10
+ //#endregion
11
+ export { CreatorArguments, parseCreatorArguments, runCreator };
package/dist/cli.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import{i as e,r as t}from"./generator.mjs";import*as n from"node:path";function r(e){let t=e.find(e=>!e.startsWith(`--`));if(t===void 0||t.startsWith(`-`))throw Error(`Usage: bunx @jchat/create-addon <addon-id> [--target=<directory>] [--image=<image>] [--workspace]`);let r=e.find(e=>e.startsWith(`--target=`)),i=e.find(e=>e.startsWith(`--image=`)),a=e.includes(`--workspace`),o=a?n.join(`addons`,t):t;return{addonId:t,targetDir:r?.slice(9)||o,sandboxImage:i?.slice(8)||void 0,workspace:a}}async function i(i){let a=await Bun.file(new URL(`../package.json`,import.meta.url)).json(),o=r(i);return o.workspace?(await t({addonId:o.addonId,targetDir:o.targetDir,isMonorepoWorkspace:!0,jchatVersion:a.version,addonSdkVersion:a.version}),n.resolve(o.targetDir)):(await e({...o,jchatVersion:a.version,addonSdkVersion:a.version,sandboxImage:o.sandboxImage??`${a.jchat?.sandboxImage??`ghcr.io/mjjabarullah/jchat-sandbox`}:${a.version.replaceAll(`+`,`-`)}`}),n.resolve(o.targetDir))}if(import.meta.main)try{let e=await i(process.argv.slice(2));console.log(`Created addon at ${e}`)}catch(e){console.error(e),process.exit(1)}export{r as parseCreatorArguments,i as runCreator};
@@ -0,0 +1,246 @@
1
+ import*as e from"node:fs";import*as t from"node:path";function n(e){try{Bun.semver.order(e,e)}catch{throw Error(`Invalid semantic version: ${e}`)}let t=/^(?:v|=)?(\d+)\./.exec(e)?.[1],n=t?Number(t):NaN;if(!Number.isSafeInteger(n))throw Error(`Invalid semantic version: ${e}`);return`>=${e} <${n+1}.0.0`}const r=/^[a-z0-9]+(?:-[a-z0-9]+)*$/;function i(e){if(!r.test(e.addonId))throw Error(`Addon id must contain lowercase letters, numbers, and hyphens`);let t={id:e.addonId,name:P(e.addonId),version:`1.0.0`,description:`${P(e.addonId)} addon for JChat`,author:`Developer`,compatibility:{jchat:n(e.jchatVersion??`1.0.0`),addonSdk:n(e.addonSdkVersion??`1.0.0`)},locales:[`ar`,`de`,`en`,`es`,`fr`,`hi`,`kn`,`ml`,`ta`,`te`,`ur`],defaultLocale:`en`,frontend:{bundle:`./ui.js`,mountPoints:[`chat-toolbar`]},permissions:[]},i=e.isMonorepoWorkspace?d(e.addonId):m(e.addonId,e.addonSdkVersion??`1.0.0`),a=e.isMonorepoWorkspace?f():h();return Object.freeze({"package.json":`${JSON.stringify(i,null,2)}\n`,"tsconfig.json":`${JSON.stringify(a,null,2)}\n`,"src/manifest.ts":g(t),"src/tailwind.css":j(),"src/locales/ar.json":_(t.name),"src/locales/de.json":v(t.name),"src/locales/en.json":y(t.name),"src/locales/es.json":b(t.name),"src/locales/fr.json":x(t.name),"src/locales/hi.json":S(t.name),"src/locales/kn.json":C(t.name),"src/locales/ml.json":w(t.name),"src/locales/ta.json":T(t.name),"src/locales/te.json":E(t.name),"src/locales/ur.json":D(t.name),"src/locales/index.ts":O(),"src/locales.test.ts":k(),"src/ui.tsx":A(t.name)})}async function a(n){let r=t.resolve(n.targetDir);if(e.existsSync(r))throw Error(`Addon target already exists: ${r}`);let a=Object.entries(i(n));for(let[n]of a){let i=t.join(r,...n.split(`/`));e.mkdirSync(t.dirname(i),{recursive:!0})}await Promise.all(a.map(([e,n])=>{let i=t.join(r,...e.split(`/`));return Bun.write(i,n)}))}function o(e){if(!r.test(e.addonId))throw Error(`Addon id must contain lowercase letters, numbers, and hyphens`);let t=e.sandboxImage??`ghcr.io/mjjabarullah/jchat-sandbox:${e.jchatVersion.replace(/\+/g,`-`)}`,n=i({addonId:e.addonId,isMonorepoWorkspace:!1,jchatVersion:e.jchatVersion,addonSdkVersion:e.addonSdkVersion}),a={".env.example":c(e.addonId,t),".gitignore":[`node_modules/`,`addon/node_modules/`,`addon/build/`,`.env`,``].join(`
2
+ `),"README.md":N(e.addonId),"docker-compose.yml":l(),"package.json":`${JSON.stringify(u(e.addonId),null,2)}\n`,"scripts/devkit.ts":M()};for(let[e,t]of Object.entries(n))a[`addon/${e}`]=t;return Object.freeze(a)}async function s(n){let r=t.resolve(n.targetDir);if(e.existsSync(r))throw Error(`Development kit target already exists: ${r}`);let i=Object.entries(o(n));for(let[n]of i){let i=t.join(r,...n.split(`/`));e.mkdirSync(t.dirname(i),{recursive:!0})}await Promise.all(i.map(([e,n])=>{let i=t.join(r,...e.split(`/`));return Bun.write(i,n)}))}function c(e,t){return[`JCHAT_SANDBOX_IMAGE=${t}`,`JCHAT_SANDBOX_ADDON_ID=${e}`,`JCHAT_SANDBOX_PORT=4317`,`JCHAT_SANDBOX_HTTP_MOCK_PORT=4318`,`JCHAT_SANDBOX_HTTP_MOCK_OFFLINE=false`,`JCHAT_SANDBOX_ADDON_HTTP_MOCK_URL=http://http-mock:4318`,``].join(`
3
+ `)}function l(){return`services:
4
+ sandbox:
5
+ image: \${JCHAT_SANDBOX_IMAGE:?Copy .env.example to .env}
6
+ restart: unless-stopped
7
+ environment:
8
+ NODE_ENV: development
9
+ JCHAT_PORT: "4317"
10
+ JCHAT_COOKIE_SECRET: addon-sandbox-cookie-secret
11
+ JCHAT_DATABASE_URL: postgresql://jchat:jchat@db:5432/jchat
12
+ JCHAT_REDIS_DATABASE_URL: redis://redis:6379
13
+ JCHAT_STORAGE_TYPE: LOCAL
14
+ JCHAT_BOT_NAME: Sandbox Bot
15
+ JCHAT_BOT_EMAIL: bot@sandbox.local
16
+ JCHAT_DOMAIN: localhost
17
+ JCHAT_SITE_URL: http://localhost:\${JCHAT_SANDBOX_PORT:-4317}
18
+ JCHAT_ROUTING_MODE: path
19
+ JCHAT_ADDON_ENCRYPTION_KEY: addon-sandbox-encryption-key
20
+ JCHAT_ADDON_HTTP_MOCK_URL: \${JCHAT_SANDBOX_ADDON_HTTP_MOCK_URL-http://http-mock:4318}
21
+ ports:
22
+ - "127.0.0.1:\${JCHAT_SANDBOX_PORT:-4317}:4317"
23
+ volumes:
24
+ - ./addon:/app/addons/\${JCHAT_SANDBOX_ADDON_ID:?Set addon id}:ro
25
+ - sandbox_uploads:/app/uploads
26
+ depends_on:
27
+ db:
28
+ condition: service_healthy
29
+ redis:
30
+ condition: service_healthy
31
+ http-mock:
32
+ condition: service_healthy
33
+
34
+ http-mock:
35
+ image: \${JCHAT_SANDBOX_IMAGE:?Copy .env.example to .env}
36
+ entrypoint: ["/app/addon-http-mock"]
37
+ environment:
38
+ JCHAT_SANDBOX_HTTP_MOCK_PORT: "4318"
39
+ JCHAT_SANDBOX_HTTP_MOCK_OFFLINE: \${JCHAT_SANDBOX_HTTP_MOCK_OFFLINE:-false}
40
+ ports:
41
+ - "127.0.0.1:\${JCHAT_SANDBOX_HTTP_MOCK_PORT:-4318}:4318"
42
+ healthcheck:
43
+ test: ["CMD", "/app/addon-http-mock", "--healthcheck"]
44
+ interval: 2s
45
+ timeout: 3s
46
+ retries: 30
47
+
48
+ db:
49
+ image: postgres:18-alpine
50
+ environment:
51
+ POSTGRES_USER: jchat
52
+ POSTGRES_PASSWORD: jchat
53
+ POSTGRES_DB: jchat
54
+ volumes:
55
+ - sandbox_postgres:/var/lib/postgresql/data
56
+ healthcheck:
57
+ test: ["CMD-SHELL", "pg_isready -U jchat"]
58
+ interval: 2s
59
+ timeout: 3s
60
+ retries: 30
61
+
62
+ redis:
63
+ image: redis:8.0.6-alpine3.21
64
+ volumes:
65
+ - sandbox_redis:/data
66
+ healthcheck:
67
+ test: ["CMD", "redis-cli", "ping"]
68
+ interval: 2s
69
+ timeout: 3s
70
+ retries: 30
71
+
72
+ volumes:
73
+ sandbox_postgres:
74
+ sandbox_redis:
75
+ sandbox_uploads:
76
+ `}function u(e){return{name:`${e}-development-kit`,private:!0,type:`module`,workspaces:[`addon`],scripts:{sandbox:`bun run ./scripts/devkit.ts run`,"sandbox:test":`bun run ./scripts/devkit.ts test`,"sandbox:reset":`bun run ./scripts/devkit.ts reset`,"sandbox:package":`bun run ./scripts/devkit.ts package`,"sandbox:cleanup":`bun run ./scripts/devkit.ts cleanup`},devDependencies:{"@types/bun":`^1.4.0`},engines:{bun:`>=1.4.0`}}}function d(e){return{name:`@jchat/${e}`,version:`1.0.0-alpha.0`,private:!0,description:`${P(e)} addon for JChat`,repository:{type:`git`,url:`git+https://github.com/mjjabarullah/jchat.git`,directory:`addons/${e}`},type:`module`,scripts:{dev:`jchat-addon dev`,build:`tsc --noEmit && jchat-addon build`,test:`bun test`,lint:`oxlint .`,format:`oxfmt . --write`,typecheck:`tsc --noEmit`},dependencies:{"lucide-react":`catalog:`,"usehooks-ts":`catalog:`},devDependencies:{"@tailwindcss/vite":`catalog:`,"@types/bun":`catalog:`,"@types/react":`catalog:`,"@types/react-dom":`catalog:`,elysia:`catalog:`,vite:`catalog:`},peerDependencies:{"@jchat/addon-sdk":`workspace:*`,"@tanstack/react-query":`catalog:`,react:`catalog:`,"react-dom":`catalog:`}}}function f(){return{extends:`@jchat/addon-sdk/tsconfig.json`,include:[`src/**/*`]}}function p(){try{let n=import.meta.dirname;for(let r=0;r<6;r++){let r=t.join(n,`package.json`);if(e.existsSync(r)){let t=e.readFileSync(r,`utf8`),n=JSON.parse(t);if(n.catalog&&typeof n.catalog==`object`)return n.catalog}let i=t.dirname(n);if(i===n)break;n=i}}catch{}return{}}function m(e,t){let n=p();return{name:`@jchat/${e}`,version:`1.0.0`,private:!0,type:`module`,scripts:{dev:`jchat-addon dev`,build:`tsc --noEmit && jchat-addon build`,test:`bun test`,typecheck:`tsc --noEmit`},dependencies:{"@jchat/addon-sdk":`^${t}`,"@tanstack/react-query":n[`@tanstack/react-query`]??`^5.101.4`,"lucide-react":n[`lucide-react`]??`1.31.0`,react:n.react??`^19.2.8`,"react-dom":n[`react-dom`]??`^19.2.8`,"usehooks-ts":n[`usehooks-ts`]??`^3.1.1`},devDependencies:{"@tailwindcss/vite":n[`@tailwindcss/vite`]??`^4.3.3`,"@types/bun":n[`@types/bun`]??`^1.4.0`,"@types/react":n[`@types/react`]??`^19.2.18`,"@types/react-dom":n[`@types/react-dom`]??`^19.2.4`,elysia:n.elysia??`^1.4.29`,tailwindcss:n.tailwindcss??`^4.3.3`,typescript:n.typescript??`^7.0.2`,vite:n.vite??`^8.2.1`}}}function h(){return{extends:`@jchat/addon-sdk/tsconfig.json`,include:[`src/**/*.ts`,`src/**/*.tsx`]}}function g(e){return`import type { AddonManifest } from "@jchat/addon-sdk"
77
+
78
+ const manifest: AddonManifest = ${JSON.stringify(e,null,2)}
79
+
80
+ export default manifest
81
+ `}function _(e){return`${JSON.stringify({welcome:`مرحبًا بك في ${e}`,mounted_at:`تم التثبيت في {{mountPoint}}`,test_button:`اختبار الإضافة`,toast_message:`مرحبًا من ${e}`},null,2)}\n`}function v(e){return`${JSON.stringify({welcome:`Willkommen bei ${e}`,mounted_at:`Eingebunden an {{mountPoint}}`,test_button:`Addon testen`,toast_message:`Hallo von ${e}`},null,2)}\n`}function y(e){return`${JSON.stringify({welcome:`Welcome to ${e}`,mounted_at:`Mounted at {{mountPoint}}`,test_button:`Test addon`,toast_message:`Hello from ${e}`},null,2)}\n`}function b(e){return`${JSON.stringify({welcome:`Bienvenido a ${e}`,mounted_at:`Montado en {{mountPoint}}`,test_button:`Probar complemento`,toast_message:`Hola desde ${e}`},null,2)}\n`}function x(e){return`${JSON.stringify({welcome:`Bienvenue sur ${e}`,mounted_at:`Monté sur {{mountPoint}}`,test_button:`Tester le plugin`,toast_message:`Bonjour de ${e}`},null,2)}\n`}function S(e){return`${JSON.stringify({welcome:`${e} में आपका स्वागत है`,mounted_at:`{{mountPoint}} पर माउंट किया गया`,test_button:`ऐडऑन का परीक्षण करें`,toast_message:`${e} की ओर से नमस्कार`},null,2)}\n`}function C(e){return`${JSON.stringify({welcome:`${e} ಗೆ ಸುಸ್ವಾಗತ`,mounted_at:`{{mountPoint}} ನಲ್ಲಿ ಸ್ಥಾಪಿಸಲಾಗಿದೆ`,test_button:`ಆಡ್ಆನ್ ಪರೀಕ್ಷಿಸಿ`,toast_message:`${e} ನಿಂದ ನಮಸ್ಕಾರ`},null,2)}\n`}function w(e){return`${JSON.stringify({welcome:`${e}-ലേക്ക് സ്വാഗതം`,mounted_at:`{{mountPoint}}-ൽ ഘടിപ്പിച്ചിരിക്കുന്നു`,test_button:`ആഡ്ഓൺ പരിശോധിക്കുക`,toast_message:`${e}-ൽ നിന്നുള്ള ആശംസകൾ`},null,2)}\n`}function T(e){return`${JSON.stringify({welcome:`${e}-க்கு வரவேற்கிறோம்`,mounted_at:`{{mountPoint}}-ல் பொருத்தப்பட்டுள்ளது`,test_button:`செருகுநிரலை சோதிக்கவும்`,toast_message:`${e}-லிருந்து வணக்கம்`},null,2)}\n`}function E(e){return`${JSON.stringify({welcome:`${e}కి స్వాగతం`,mounted_at:`{{mountPoint}} వద్ద మౌంట్ చేయబడింది`,test_button:`యాడ్ఆన్‌ని పరీక్షించండి`,toast_message:`${e} నుండి నమస్కారం`},null,2)}\n`}function D(e){return`${JSON.stringify({welcome:`${e} میں خوش آمدید`,mounted_at:`{{mountPoint}} پر نصب ہے`,test_button:`ایڈ آن کا تجربہ کریں`,toast_message:`${e} کی طرف سے ہیلو`},null,2)}\n`}function O(){return`import ar from "./ar.json"
82
+ import de from "./de.json"
83
+ import en from "./en.json"
84
+ import es from "./es.json"
85
+ import fr from "./fr.json"
86
+ import hi from "./hi.json"
87
+ import kn from "./kn.json"
88
+ import ml from "./ml.json"
89
+ import ta from "./ta.json"
90
+ import te from "./te.json"
91
+ import ur from "./ur.json"
92
+
93
+ export type TranslationKey = keyof typeof en
94
+
95
+ export const locales = {
96
+ ar,
97
+ de,
98
+ en,
99
+ es,
100
+ fr,
101
+ hi,
102
+ kn,
103
+ ml,
104
+ ta,
105
+ te,
106
+ ur
107
+ } as const
108
+
109
+ export default locales
110
+ `}function k(){return`import { describe, expect, test } from "bun:test"
111
+ import { validateAddonLocales } from "@jchat/addon-sdk"
112
+ import * as path from "node:path"
113
+
114
+ describe("addon locales", () => {
115
+ test("maintains key parity across all 11 host languages", () => {
116
+ const localesDir = path.join(import.meta.dir, "locales")
117
+ const result = validateAddonLocales(localesDir)
118
+ if (!result.valid) {
119
+ console.error(result.errors.join("\\n"))
120
+ }
121
+ expect(result.valid).toBe(true)
122
+ })
123
+ })
124
+ `}function A(e){return`import type { AddonProps } from "@jchat/addon-sdk"
125
+ import { useAddonSDK, useIcons, useTranslation } from "@jchat/addon-sdk"
126
+ import locales from "./locales"
127
+ import "./tailwind.css"
128
+
129
+ export default function Addon({ mountPoint }: AddonProps) {
130
+ const sdk = useAddonSDK()
131
+ const { Icons } = useIcons()
132
+ const { t, dir } = useTranslation(locales)
133
+
134
+ return (
135
+ <div dir={dir} className="rounded border border-border bg-card p-2 text-card-foreground">
136
+ <div className="mb-2 flex items-center gap-2">
137
+ <Icons.Plus aria-hidden="true" className="size-4 text-primary" />
138
+ <strong>${e}</strong>
139
+ </div>
140
+ <p className="mb-2 text-sm text-muted-foreground">{t("mounted_at", { mountPoint })}</p>
141
+ <button
142
+ type="button"
143
+ className="rounded bg-primary px-3 py-2 text-primary-foreground"
144
+ onClick={() => sdk.toast(t("toast_message", { name: "${e}" }))}
145
+ >
146
+ {t("test_button")}
147
+ </button>
148
+ </div>
149
+ )
150
+ }
151
+ `}function j(){return`@import "@jchat/addon-sdk/ui/tailwind.css";`}function M(){return`import * as fs from "node:fs"
152
+ import * as path from "node:path"
153
+
154
+ type Command = "run" | "test" | "reset" | "package" | "cleanup"
155
+
156
+ const command = (process.argv[2] ?? "run") as Command
157
+ if (!["run", "test", "reset", "package", "cleanup"].includes(command)) {
158
+ throw new Error(\`Unknown development-kit command: \${command}\`)
159
+ }
160
+
161
+ const root = path.resolve(import.meta.dir, "..")
162
+ const environment = {
163
+ ...process.env,
164
+ ...readEnvironment(path.join(root, ".env"))
165
+ }
166
+ const addonId = environment.JCHAT_SANDBOX_ADDON_ID
167
+ if (!addonId) throw new Error("JCHAT_SANDBOX_ADDON_ID is missing from .env")
168
+ const compose = [
169
+ "docker",
170
+ "compose",
171
+ "--project-name",
172
+ \`jchat-addon-devkit-\${addonId}\`,
173
+ "--file",
174
+ path.join(root, "docker-compose.yml")
175
+ ]
176
+
177
+ if (command === "test") {
178
+ await run(["bun", "test"], path.join(root, "addon"), environment)
179
+ } else if (command === "package") {
180
+ await run(["bun", "run", "build"], path.join(root, "addon"), environment)
181
+ } else if (command === "cleanup") {
182
+ await run([...compose, "down", "--remove-orphans", "--volumes"], root, environment)
183
+ } else {
184
+ if (command === "reset") {
185
+ await run([...compose, "down", "--remove-orphans", "--volumes"], root, environment)
186
+ }
187
+ await run(["bun", "run", "build"], path.join(root, "addon"), environment)
188
+ const watcher = Bun.spawn(["bun", "run", "dev"], {
189
+ cwd: path.join(root, "addon"),
190
+ env: environment,
191
+ stdout: "inherit",
192
+ stderr: "inherit"
193
+ })
194
+ try {
195
+ await run([...compose, "up"], root, environment)
196
+ } finally {
197
+ watcher.kill()
198
+ await watcher.exited
199
+ }
200
+ }
201
+
202
+ async function run(command: string[], cwd: string, env: Record<string, string | undefined>) {
203
+ const child = Bun.spawn(command, { cwd, env, stdin: "inherit", stdout: "inherit", stderr: "inherit" })
204
+ const exitCode = await child.exited
205
+ if (exitCode !== 0) throw new Error(\`\${command.join(" ")} exited with code \${exitCode}\`)
206
+ }
207
+
208
+ function readEnvironment(file: string): Record<string, string> {
209
+ if (!fs.existsSync(file)) throw new Error("Copy .env.example to .env before running the kit")
210
+ return Object.fromEntries(
211
+ fs.readFileSync(file, "utf8")
212
+ .split(/\\r?\\n/)
213
+ .filter((line) => line && !line.startsWith("#"))
214
+ .map((line) => {
215
+ const separator = line.indexOf("=")
216
+ return [line.slice(0, separator), line.slice(separator + 1)]
217
+ })
218
+ )
219
+ }
220
+ `}function N(e){return`# ${P(e)} Development Kit
221
+
222
+ This workspace develops \`${e}\` against the license-free JChat developer sandbox without
223
+ including JChat Core source.
224
+
225
+ ## Start
226
+
227
+ \`\`\`powershell
228
+ Copy-Item .env.example .env
229
+ bun install
230
+ bun sandbox
231
+ \`\`\`
232
+
233
+ Open \`http://127.0.0.1:4317\`. The HTTP fixture recorder is available at
234
+ \`http://127.0.0.1:4318/requests\`.
235
+
236
+ ## Commands
237
+
238
+ - \`bun sandbox\`
239
+ - \`bun sandbox:test\`
240
+ - \`bun sandbox:reset\`
241
+ - \`bun sandbox:package\`
242
+ - \`bun sandbox:cleanup\`
243
+
244
+ The referenced JChat packages and sandbox image must be published before this standalone kit can
245
+ install or start outside the JChat repository.
246
+ `}function P(e){return e.split(`-`).map(e=>`${e[0]?.toUpperCase()??``}${e.slice(1)}`).join(` `)}export{s as i,o as n,a as r,i as t};
@@ -0,0 +1,26 @@
1
+ //#region src/generator.d.ts
2
+ interface StandaloneDevkitOptions {
3
+ addonId: string;
4
+ targetDir: string;
5
+ jchatVersion: string;
6
+ addonSdkVersion: string;
7
+ sandboxImage?: string;
8
+ }
9
+ interface AddonWorkspaceOptions {
10
+ addonId: string;
11
+ targetDir: string;
12
+ isMonorepoWorkspace?: boolean;
13
+ jchatVersion?: string;
14
+ addonSdkVersion?: string;
15
+ }
16
+ declare function createAddonFiles(options: {
17
+ addonId: string;
18
+ isMonorepoWorkspace?: boolean;
19
+ jchatVersion?: string;
20
+ addonSdkVersion?: string;
21
+ }): Readonly<Record<string, string>>;
22
+ declare function writeAddonWorkspace(options: AddonWorkspaceOptions): Promise<void>;
23
+ declare function createStandaloneDevkitFiles(options: StandaloneDevkitOptions): Readonly<Record<string, string>>;
24
+ declare function writeStandaloneDevkit(options: StandaloneDevkitOptions): Promise<void>;
25
+ //#endregion
26
+ export { type AddonWorkspaceOptions, type StandaloneDevkitOptions, createAddonFiles, createStandaloneDevkitFiles, writeAddonWorkspace, writeStandaloneDevkit };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{i as e,n as t,r as n,t as r}from"./generator.mjs";export{r as createAddonFiles,t as createStandaloneDevkitFiles,n as writeAddonWorkspace,e as writeStandaloneDevkit};
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@jchat/create-addon",
3
+ "version": "1.0.0-alpha.10",
4
+ "private": false,
5
+ "description": "Command line tool for jchat addon creation.",
6
+ "keywords": [
7
+ "addon",
8
+ "chat",
9
+ "cli",
10
+ "create",
11
+ "jchat",
12
+ "plugin",
13
+ "scaffold"
14
+ ],
15
+ "license": "SEE LICENSE IN LICENSE",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/mjjabarullah/jchat.git",
19
+ "directory": "packages/create-addon"
20
+ },
21
+ "bin": {
22
+ "create-addon": "./dist/cli.mjs"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md",
27
+ "CHANGELOG.md"
28
+ ],
29
+ "type": "module",
30
+ "main": "./dist/index.mjs",
31
+ "types": "./dist/index.d.mts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.mts",
35
+ "import": "./dist/index.mjs"
36
+ }
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "build": "tsdown",
43
+ "test": "bun test",
44
+ "lint": "oxlint .",
45
+ "format": "oxfmt . --write",
46
+ "typecheck": "tsc --noEmit",
47
+ "prepack": "bun run build"
48
+ },
49
+ "devDependencies": {
50
+ "@jchat/addon-sdk": "workspace:*",
51
+ "@types/bun": "catalog:",
52
+ "tsdown": "^0.22.14"
53
+ },
54
+ "engines": {
55
+ "bun": ">=1.4.0"
56
+ },
57
+ "jchat": {
58
+ "sandboxImage": "ghcr.io/mjjabarullah/jchat-sandbox"
59
+ }
60
+ }