@jchat/addon-sdk 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +13 -0
- package/README.md +24 -0
- package/dist/diagnostics.d.mts +47 -0
- package/dist/diagnostics.mjs +65 -0
- package/dist/index.d.mts +15 -1
- package/dist/index.mjs +20 -1
- package/dist/vite.d.mts +55 -2
- package/dist/vite.mjs +49 -3
- package/package.json +2 -3
- package/src/contracts.d.ts +0 -127
- package/src/contracts.ts +0 -132
- package/src/diagnostics.d.ts +0 -19
- package/src/diagnostics.test.ts +0 -106
- package/src/diagnostics.ts +0 -182
- package/src/index.d.ts +0 -249
- package/src/index.test.ts +0 -35
- package/src/index.ts +0 -299
- package/src/vite.test.ts +0 -62
- package/src/vite.ts +0 -289
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @jchat/addon-sdk
|
|
2
2
|
|
|
3
|
+
## 1.0.0-alpha.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ada430f: Formatted type declarations in the Addon SDK to align with project style guidelines.
|
|
8
|
+
|
|
9
|
+
## 1.0.0-alpha.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f7a80fc: - Added `getAddonSdkVersion()` utility export to allow addons and the sandbox environment to safely resolve the runtime SDK version.
|
|
14
|
+
- Updated sandbox developer scripts and Dockerfiles to correctly mount and execute the development environment.
|
|
15
|
+
|
|
3
16
|
## 1.0.0-alpha.2
|
|
4
17
|
|
|
5
18
|
## 1.0.0-alpha.1
|
package/README.md
CHANGED
|
@@ -40,6 +40,30 @@ export default {
|
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## Database Architecture & Schema Isolation
|
|
44
|
+
|
|
45
|
+
JChat database-bearing addons use **Kysely** for server-side database access:
|
|
46
|
+
|
|
47
|
+
- **0 Bundle Overhead**: Table definitions are pure TypeScript interfaces (`src/db.ts`), resulting in 0 runtime JavaScript bundle footprint.
|
|
48
|
+
- **Shared `addons` Schema**: All addon tables are placed inside the dedicated `"addons"` schema (e.g. `addons.DjDropTracks`, `addons.PollwisePolls`, `addons.GlimpseStories`), keeping them cleanly isolated from core `public` application tables without requiring dynamic PostgreSQL role creation (`CREATE ROLE`).
|
|
49
|
+
- **Mandatory Table Naming**: Table names **must start with a Capital letter** and **must be prefixed with your capitalized `addonId`** (e.g. `addons.MyAddonSettings`). Unprefixed tables are rejected by the host migration validator.
|
|
50
|
+
- **SQL Migrations**: Addon migrations are stored as plain SQL files (`migrations/001_*.sql`) listed in `manifest.ts` under `server.migrations`.
|
|
51
|
+
- **ID Column Convention**: Use `text` / `varchar(36)` for host-referencing IDs (`siteId`, `roomId`, `userId`) to ensure driver-agnostic, cast-free query execution with UUID v7 strings.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import type { AddonDatabase } from "@jchat/addon-sdk"
|
|
55
|
+
|
|
56
|
+
export interface MyAddonDatabase {
|
|
57
|
+
MyAddonSettings: {
|
|
58
|
+
id: string
|
|
59
|
+
siteId: string
|
|
60
|
+
createdAt: Date
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type MyAddonContext = AddonServerContext<MyAddonDatabase>
|
|
65
|
+
```
|
|
66
|
+
|
|
43
67
|
## License
|
|
44
68
|
|
|
45
69
|
Apache-2.0
|
package/dist/diagnostics.d.mts
CHANGED
|
@@ -1,22 +1,69 @@
|
|
|
1
1
|
import { t as AddonManifest } from "./contracts.mjs";
|
|
2
2
|
//#region src/diagnostics.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Diagnostic result status indicator for addon validation checks. - `"pass"`: The diagnostic check
|
|
5
|
+
* passed successfully. - `"warning"`: Non-critical warning (e.g. source maps present in
|
|
6
|
+
* production). - `"error"`: Critical failure blocking addon installation or distribution.
|
|
7
|
+
*/
|
|
3
8
|
type AddonDiagnosticStatus = "pass" | "warning" | "error";
|
|
9
|
+
/** Represents an individual diagnostic check result for an addon package. */
|
|
4
10
|
interface AddonPackageDiagnostic {
|
|
11
|
+
/** Unique identifier for the diagnostic check (e.g. `"jchat"`, `"package-size"`). */
|
|
5
12
|
id: string;
|
|
13
|
+
/** Human-readable title for the diagnostic check. */
|
|
6
14
|
label: string;
|
|
15
|
+
/** Severity status outcome of the check. */
|
|
7
16
|
status: AddonDiagnosticStatus;
|
|
17
|
+
/** Descriptive message or metric value providing check details. */
|
|
8
18
|
message: string;
|
|
9
19
|
}
|
|
20
|
+
/** Options required to run diagnostic validation on an addon build package. */
|
|
10
21
|
interface AddonPackageDiagnosticOptions {
|
|
22
|
+
/** Absolute path to the compiled addon build output directory (`build/`). */
|
|
11
23
|
buildDir: string;
|
|
24
|
+
/** Parsed addon manifest object (`manifest.json`). */
|
|
12
25
|
manifest: AddonManifest;
|
|
26
|
+
/** Semantic version of the host JChat platform instance. */
|
|
13
27
|
jchatVersion: string;
|
|
28
|
+
/** Semantic version of the `@jchat/addon-sdk` package. */
|
|
14
29
|
addonSdkVersion: string;
|
|
30
|
+
/** Whether the diagnostic target is a production `.zip` package build. Defaults to `false`. */
|
|
15
31
|
productionPackage?: boolean;
|
|
32
|
+
/** Maximum allowable package size in bytes. Defaults to 25MB (`26,214,400` bytes). */
|
|
16
33
|
maxBytes?: number;
|
|
17
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Constructs a semantic version compatibility range covering all minor/patch versions of the
|
|
37
|
+
* current major version.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* createCompatibleMajorRange("1.4.2") // returns ">=1.4.2 <2.0.0"
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param version - Valid semver string (e.g. `"1.2.0"`).
|
|
45
|
+
* @returns Semver range string matching the current major series (e.g. `">=1.2.0 <2.0.0"`).
|
|
46
|
+
* @throws {@link Error} If the input is not a valid semantic version string.
|
|
47
|
+
*/
|
|
18
48
|
declare function createCompatibleMajorRange(version: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Performs comprehensive diagnostic validation checks on an addon package build directory.
|
|
51
|
+
*
|
|
52
|
+
* Validates version compatibility ranges, verifies required build files (frontend/server bundles,
|
|
53
|
+
* migrations, seeds), checks total package size limits, flags production source maps, and verifies
|
|
54
|
+
* publisher digital signatures.
|
|
55
|
+
*
|
|
56
|
+
* @param options - Configuration options specifying build directory, manifest, and environment
|
|
57
|
+
* versions.
|
|
58
|
+
* @returns Array of {@link AddonPackageDiagnostic} results.
|
|
59
|
+
*/
|
|
19
60
|
declare function diagnoseAddonPackage(options: AddonPackageDiagnosticOptions): AddonPackageDiagnostic[];
|
|
61
|
+
/**
|
|
62
|
+
* Checks whether any diagnostic check in a diagnostic list returned a critical error.
|
|
63
|
+
*
|
|
64
|
+
* @param diagnostics - Array of diagnostic check results to evaluate.
|
|
65
|
+
* @returns `true` if at least one check has `status === "error"`, otherwise `false`.
|
|
66
|
+
*/
|
|
20
67
|
declare function hasAddonDiagnosticErrors(diagnostics: readonly AddonPackageDiagnostic[]): boolean;
|
|
21
68
|
//#endregion
|
|
22
69
|
export { AddonDiagnosticStatus, AddonPackageDiagnostic, AddonPackageDiagnosticOptions, createCompatibleMajorRange, diagnoseAddonPackage, hasAddonDiagnosticErrors };
|
package/dist/diagnostics.mjs
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import * as fs$1 from "node:fs";
|
|
2
2
|
import * as path$1 from "node:path";
|
|
3
3
|
//#region src/diagnostics.ts
|
|
4
|
+
/**
|
|
5
|
+
* Constructs a semantic version compatibility range covering all minor/patch versions of the
|
|
6
|
+
* current major version.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* createCompatibleMajorRange("1.4.2") // returns ">=1.4.2 <2.0.0"
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @param version - Valid semver string (e.g. `"1.2.0"`).
|
|
14
|
+
* @returns Semver range string matching the current major series (e.g. `">=1.2.0 <2.0.0"`).
|
|
15
|
+
* @throws {@link Error} If the input is not a valid semantic version string.
|
|
16
|
+
*/
|
|
4
17
|
function createCompatibleMajorRange(version) {
|
|
5
18
|
try {
|
|
6
19
|
Bun.semver.order(version, version);
|
|
@@ -12,6 +25,17 @@ function createCompatibleMajorRange(version) {
|
|
|
12
25
|
if (!Number.isSafeInteger(major)) throw new Error(`Invalid semantic version: ${version}`);
|
|
13
26
|
return `>=${version} <${major + 1}.0.0`;
|
|
14
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Performs comprehensive diagnostic validation checks on an addon package build directory.
|
|
30
|
+
*
|
|
31
|
+
* Validates version compatibility ranges, verifies required build files (frontend/server bundles,
|
|
32
|
+
* migrations, seeds), checks total package size limits, flags production source maps, and verifies
|
|
33
|
+
* publisher digital signatures.
|
|
34
|
+
*
|
|
35
|
+
* @param options - Configuration options specifying build directory, manifest, and environment
|
|
36
|
+
* versions.
|
|
37
|
+
* @returns Array of {@link AddonPackageDiagnostic} results.
|
|
38
|
+
*/
|
|
15
39
|
function diagnoseAddonPackage(options) {
|
|
16
40
|
const { buildDir, manifest } = options;
|
|
17
41
|
const diagnostics = [];
|
|
@@ -59,6 +83,18 @@ function diagnoseAddonPackage(options) {
|
|
|
59
83
|
status: totalBytes <= maxBytes ? "pass" : "error",
|
|
60
84
|
message: `${totalBytes} of ${maxBytes} bytes`
|
|
61
85
|
});
|
|
86
|
+
const addonPermPrefix = `addon.${manifest.id}.`;
|
|
87
|
+
const invalidPerms = Array.from(/* @__PURE__ */ new Set([
|
|
88
|
+
...manifest.accessPermission ? [manifest.accessPermission] : [],
|
|
89
|
+
...manifest.permissions ?? [],
|
|
90
|
+
...manifest.permissionDefinitions?.map((d) => d.name) ?? []
|
|
91
|
+
])).filter((perm) => !perm.startsWith(addonPermPrefix));
|
|
92
|
+
diagnostics.push({
|
|
93
|
+
id: "permission-namespacing",
|
|
94
|
+
label: "Permission namespacing",
|
|
95
|
+
status: invalidPerms.length === 0 ? "pass" : "error",
|
|
96
|
+
message: invalidPerms.length === 0 ? `All declared permissions start with ${addonPermPrefix}` : `Permission(s) do not follow naming convention ${addonPermPrefix}*: ${invalidPerms.join(", ")}`
|
|
97
|
+
});
|
|
62
98
|
const sourceMaps = files.filter((file) => file.endsWith(".map"));
|
|
63
99
|
diagnostics.push({
|
|
64
100
|
id: "source-maps",
|
|
@@ -81,9 +117,24 @@ function diagnoseAddonPackage(options) {
|
|
|
81
117
|
});
|
|
82
118
|
return diagnostics;
|
|
83
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Checks whether any diagnostic check in a diagnostic list returned a critical error.
|
|
122
|
+
*
|
|
123
|
+
* @param diagnostics - Array of diagnostic check results to evaluate.
|
|
124
|
+
* @returns `true` if at least one check has `status === "error"`, otherwise `false`.
|
|
125
|
+
*/
|
|
84
126
|
function hasAddonDiagnosticErrors(diagnostics) {
|
|
85
127
|
return diagnostics.some(({ status }) => status === "error");
|
|
86
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Evaluates semantic version compatibility between a target version and a semver range string.
|
|
131
|
+
*
|
|
132
|
+
* @param id - Diagnostic check identifier.
|
|
133
|
+
* @param label - Diagnostic check label.
|
|
134
|
+
* @param version - Environment component version string.
|
|
135
|
+
* @param range - Expected semver range string from manifest compatibility declarations.
|
|
136
|
+
* @returns An {@link AddonPackageDiagnostic} result object.
|
|
137
|
+
*/
|
|
87
138
|
function compatibilityDiagnostic(id, label, version, range) {
|
|
88
139
|
const compatible = Boolean(range && Bun.semver.satisfies(version, range));
|
|
89
140
|
return {
|
|
@@ -93,12 +144,26 @@ function compatibilityDiagnostic(id, label, version, range) {
|
|
|
93
144
|
message: range ? `${version} ${compatible ? "satisfies" : "does not satisfy"} ${range}` : "Compatibility range is missing"
|
|
94
145
|
};
|
|
95
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Safely resolves a relative build file reference within a target build directory, guarding against
|
|
149
|
+
* directory traversal attacks.
|
|
150
|
+
*
|
|
151
|
+
* @param buildDir - Absolute path to the build directory.
|
|
152
|
+
* @param reference - Relative file path declared in manifest.
|
|
153
|
+
* @returns Resolved absolute file path if safe and inside `buildDir`, or `null` if unsafe.
|
|
154
|
+
*/
|
|
96
155
|
function resolveBuildFile(buildDir, reference) {
|
|
97
156
|
if (path$1.isAbsolute(reference) || reference.includes("\\") || reference.split("/").includes("..")) return null;
|
|
98
157
|
const target = path$1.resolve(buildDir, ...reference.replace(/^\.\//, "").split("/"));
|
|
99
158
|
const relative = path$1.relative(path$1.resolve(buildDir), target);
|
|
100
159
|
return relative && !relative.startsWith("..") && !path$1.isAbsolute(relative) ? target : null;
|
|
101
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Recursively lists all regular files within a root directory.
|
|
163
|
+
*
|
|
164
|
+
* @param root - Path to search.
|
|
165
|
+
* @returns Array of absolute file paths.
|
|
166
|
+
*/
|
|
102
167
|
function listRegularFiles(root) {
|
|
103
168
|
if (!fs$1.existsSync(root)) return [];
|
|
104
169
|
return fs$1.readdirSync(root, {
|
package/dist/index.d.mts
CHANGED
|
@@ -253,5 +253,19 @@ declare function useAddonSDK(): AddonSDK;
|
|
|
253
253
|
declare function useIcons(): {
|
|
254
254
|
Icons: IconPack;
|
|
255
255
|
};
|
|
256
|
+
/**
|
|
257
|
+
* Get the version of the addon SDK. This is useful for checking compatibility with the host
|
|
258
|
+
* application.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ;```tsx
|
|
262
|
+
* import { getAddonSdkVersion } from "@jchat/addon-sdk"
|
|
263
|
+
*
|
|
264
|
+
* console.log(getAddonSdkVersion())
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* @returns
|
|
268
|
+
*/
|
|
269
|
+
declare function getAddonSdkVersion(): string;
|
|
256
270
|
//#endregion
|
|
257
|
-
export { AddonApi, AddonDatabase, AddonEventListener, AddonFont, AddonJsonBody, type AddonManifest, type AddonPermission, type AddonPermissionDefinition, AddonProps, AddonRequestUser, AddonSDK, AddonSeedDocument, AddonServerContext, AddonServerFactory, AddonUser, type ConfigSchemaField, MOUNT_POINTS, type MountPoint, defineAddonManifest, defineAddonServer, useAddonSDK, useIcons };
|
|
271
|
+
export { AddonApi, AddonDatabase, AddonEventListener, AddonFont, AddonJsonBody, type AddonManifest, type AddonPermission, type AddonPermissionDefinition, AddonProps, AddonRequestUser, AddonSDK, AddonSeedDocument, AddonServerContext, AddonServerFactory, AddonUser, type ConfigSchemaField, MOUNT_POINTS, type MountPoint, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonSDK, useIcons };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
//#region package.json
|
|
2
|
+
var version = "1.0.0-alpha.4";
|
|
3
|
+
//#endregion
|
|
1
4
|
//#region src/contracts.ts
|
|
2
5
|
/** Valid locations where an addon can be rendered within the Jchat layout. */
|
|
3
6
|
const MOUNT_POINTS = {
|
|
@@ -82,5 +85,21 @@ function useAddonSDK() {
|
|
|
82
85
|
function useIcons() {
|
|
83
86
|
throw new Error("useIcons must be used within the Jchat host environment.");
|
|
84
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the version of the addon SDK. This is useful for checking compatibility with the host
|
|
90
|
+
* application.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ;```tsx
|
|
94
|
+
* import { getAddonSdkVersion } from "@jchat/addon-sdk"
|
|
95
|
+
*
|
|
96
|
+
* console.log(getAddonSdkVersion())
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @returns
|
|
100
|
+
*/
|
|
101
|
+
function getAddonSdkVersion() {
|
|
102
|
+
return version;
|
|
103
|
+
}
|
|
85
104
|
//#endregion
|
|
86
|
-
export { MOUNT_POINTS, defineAddonManifest, defineAddonServer, useAddonSDK, useIcons };
|
|
105
|
+
export { MOUNT_POINTS, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonSDK, useIcons };
|
package/dist/vite.d.mts
CHANGED
|
@@ -2,13 +2,41 @@ import { t as AddonManifest } from "./contracts.mjs";
|
|
|
2
2
|
import { Plugin } from "vite";
|
|
3
3
|
//#region src/vite.d.ts
|
|
4
4
|
/**
|
|
5
|
-
* Vite plugin
|
|
6
|
-
*
|
|
5
|
+
* Vite plugin for building JChat sandboxed addon extensions.
|
|
6
|
+
*
|
|
7
|
+
* Configures automatic manifest generation (`manifest.json`), bundles server-side API scripts
|
|
8
|
+
* (`src/api.ts`), embeds scoped Tailwind CSS into the frontend bundle, packages database migrations
|
|
9
|
+
* and seed files, copies documentation (`README.md`), signs database-bearing addon manifests, and
|
|
10
|
+
* zips production build artifacts.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ;```ts
|
|
14
|
+
* import { jchatAddon } from "@jchat/addon-sdk/vite"
|
|
15
|
+
* import manifest from "./src/manifest"
|
|
16
|
+
* import { defineConfig } from "vite"
|
|
17
|
+
*
|
|
18
|
+
* export default defineConfig({
|
|
19
|
+
* plugins: [jchatAddon({ manifest })]
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param options - Configuration options for the JChat addon Vite plugin.
|
|
24
|
+
* @returns A Vite {@link Plugin} instance configured for addon compilation.
|
|
7
25
|
*/
|
|
8
26
|
declare function jchatAddon(options: JchatAddonPluginOptions): Plugin;
|
|
27
|
+
/** Configuration options for the {@link jchatAddon} Vite plugin. */
|
|
9
28
|
interface JchatAddonPluginOptions {
|
|
29
|
+
/** The base manifest specification for the addon. */
|
|
10
30
|
manifest: AddonManifest;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to package the build output into a `.zip` archive upon build completion. Defaults to
|
|
33
|
+
* `true` during production builds and `false` during development builds.
|
|
34
|
+
*/
|
|
11
35
|
zip?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Optional RSA private key string used to cryptographically sign database-bearing addons. If
|
|
38
|
+
* omitted, falls back to the `JCHAT_ADDON_SIGNING_PRIVATE_KEY` environment variable.
|
|
39
|
+
*/
|
|
12
40
|
signingPrivateKey?: string;
|
|
13
41
|
/**
|
|
14
42
|
* Embed Tailwind CSS output into the JS bundle and scope it to the addon's DOM container.
|
|
@@ -16,11 +44,36 @@ interface JchatAddonPluginOptions {
|
|
|
16
44
|
*/
|
|
17
45
|
css?: boolean;
|
|
18
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Generates CLI argument parameters for bundling server-side Elysia API scripts (`src/api.ts`) via
|
|
49
|
+
* Bun.
|
|
50
|
+
*
|
|
51
|
+
* @param entryPath - Absolute file path to the server entry script (`src/api.ts`).
|
|
52
|
+
* @param outputPath - Absolute destination file path for the bundled output (`build/api.js`).
|
|
53
|
+
* @param developmentBuild - Indicates whether to enable external sourcemaps or minification.
|
|
54
|
+
* @returns Array of command line arguments for running `bun build`.
|
|
55
|
+
*/
|
|
19
56
|
declare function createAddonServerBuildArguments(entryPath: string, outputPath: string, developmentBuild: boolean): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Cryptographically signs an addon manifest using RSA-SHA256 over all archive file contents.
|
|
59
|
+
*
|
|
60
|
+
* @param manifest - Unsigned or base addon manifest.
|
|
61
|
+
* @param files - List of archive file paths and raw contents to digest.
|
|
62
|
+
* @param privateKey - RSA private key in PEM format.
|
|
63
|
+
* @returns Updated {@link AddonManifest} containing an RSA signature block.
|
|
64
|
+
*/
|
|
20
65
|
declare function signAddonManifest(manifest: AddonManifest, files: readonly {
|
|
21
66
|
path: string;
|
|
22
67
|
contents: Buffer;
|
|
23
68
|
}[], privateKey: string): AddonManifest;
|
|
69
|
+
/**
|
|
70
|
+
* Resolves the required RSA signing key for database-bearing addons.
|
|
71
|
+
*
|
|
72
|
+
* @param signingPrivateKey - Provided signing key or undefined.
|
|
73
|
+
* @param developmentBuild - Whether the current build is targeting development mode.
|
|
74
|
+
* @returns Resolved signing key string or undefined (in dev builds).
|
|
75
|
+
* @throws {@link Error} If building a database-bearing addon in production without a signing key.
|
|
76
|
+
*/
|
|
24
77
|
declare function requireAddonSigningKey(signingPrivateKey: string | undefined, developmentBuild: boolean): string | undefined;
|
|
25
78
|
//#endregion
|
|
26
79
|
export { JchatAddonPluginOptions, createAddonServerBuildArguments, jchatAddon, requireAddonSigningKey, signAddonManifest };
|
package/dist/vite.mjs
CHANGED
|
@@ -4,8 +4,26 @@ import { execFileSync } from "node:child_process";
|
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
5
|
//#region src/vite.ts
|
|
6
6
|
/**
|
|
7
|
-
* Vite plugin
|
|
8
|
-
*
|
|
7
|
+
* Vite plugin for building JChat sandboxed addon extensions.
|
|
8
|
+
*
|
|
9
|
+
* Configures automatic manifest generation (`manifest.json`), bundles server-side API scripts
|
|
10
|
+
* (`src/api.ts`), embeds scoped Tailwind CSS into the frontend bundle, packages database migrations
|
|
11
|
+
* and seed files, copies documentation (`README.md`), signs database-bearing addon manifests, and
|
|
12
|
+
* zips production build artifacts.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ;```ts
|
|
16
|
+
* import { jchatAddon } from "@jchat/addon-sdk/vite"
|
|
17
|
+
* import manifest from "./src/manifest"
|
|
18
|
+
* import { defineConfig } from "vite"
|
|
19
|
+
*
|
|
20
|
+
* export default defineConfig({
|
|
21
|
+
* plugins: [jchatAddon({ manifest })]
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param options - Configuration options for the JChat addon Vite plugin.
|
|
26
|
+
* @returns A Vite {@link Plugin} instance configured for addon compilation.
|
|
9
27
|
*/
|
|
10
28
|
function jchatAddon(options) {
|
|
11
29
|
const { manifest: manifestData } = options;
|
|
@@ -81,12 +99,15 @@ function jchatAddon(options) {
|
|
|
81
99
|
})), signingPrivateKey);
|
|
82
100
|
}
|
|
83
101
|
}
|
|
102
|
+
const readmeSource = path.resolve(rootDir, "README.md");
|
|
103
|
+
if (fs.existsSync(readmeSource)) fs.copyFileSync(readmeSource, path.resolve(buildDir, "README.md"));
|
|
84
104
|
const manifestJson = JSON.stringify(manifest, null, 2);
|
|
85
105
|
fs.writeFileSync(path.resolve(buildDir, "manifest.json"), manifestJson);
|
|
86
106
|
if (shouldZip) {
|
|
87
107
|
const zipName = `${manifest.id}-${pkg.version}.zip`;
|
|
88
108
|
const filesToZip = ["manifest.json", bundleFile];
|
|
89
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");
|
|
90
111
|
const migrationsDest = path.resolve(buildDir, "migrations");
|
|
91
112
|
if (fs.existsSync(migrationsDest)) filesToZip.push("migrations");
|
|
92
113
|
const seedsDest = path.resolve(buildDir, "seeds");
|
|
@@ -105,6 +126,15 @@ function jchatAddon(options) {
|
|
|
105
126
|
}
|
|
106
127
|
};
|
|
107
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Generates CLI argument parameters for bundling server-side Elysia API scripts (`src/api.ts`) via
|
|
131
|
+
* Bun.
|
|
132
|
+
*
|
|
133
|
+
* @param entryPath - Absolute file path to the server entry script (`src/api.ts`).
|
|
134
|
+
* @param outputPath - Absolute destination file path for the bundled output (`build/api.js`).
|
|
135
|
+
* @param developmentBuild - Indicates whether to enable external sourcemaps or minification.
|
|
136
|
+
* @returns Array of command line arguments for running `bun build`.
|
|
137
|
+
*/
|
|
108
138
|
function createAddonServerBuildArguments(entryPath, outputPath, developmentBuild) {
|
|
109
139
|
return [
|
|
110
140
|
"build",
|
|
@@ -112,9 +142,17 @@ function createAddonServerBuildArguments(entryPath, outputPath, developmentBuild
|
|
|
112
142
|
"--target=bun",
|
|
113
143
|
"--format=esm",
|
|
114
144
|
`--outfile=${outputPath}`,
|
|
115
|
-
developmentBuild ? "--sourcemap=
|
|
145
|
+
developmentBuild ? "--sourcemap=inline" : "--minify"
|
|
116
146
|
];
|
|
117
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Cryptographically signs an addon manifest using RSA-SHA256 over all archive file contents.
|
|
150
|
+
*
|
|
151
|
+
* @param manifest - Unsigned or base addon manifest.
|
|
152
|
+
* @param files - List of archive file paths and raw contents to digest.
|
|
153
|
+
* @param privateKey - RSA private key in PEM format.
|
|
154
|
+
* @returns Updated {@link AddonManifest} containing an RSA signature block.
|
|
155
|
+
*/
|
|
118
156
|
function signAddonManifest(manifest, files, privateKey) {
|
|
119
157
|
if (!manifest.publisher?.id) throw new Error("Signed addons require manifest.publisher.id");
|
|
120
158
|
const unsigned = {
|
|
@@ -142,6 +180,14 @@ function signAddonManifest(manifest, files, privateKey) {
|
|
|
142
180
|
}
|
|
143
181
|
};
|
|
144
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Resolves the required RSA signing key for database-bearing addons.
|
|
185
|
+
*
|
|
186
|
+
* @param signingPrivateKey - Provided signing key or undefined.
|
|
187
|
+
* @param developmentBuild - Whether the current build is targeting development mode.
|
|
188
|
+
* @returns Resolved signing key string or undefined (in dev builds).
|
|
189
|
+
* @throws {@link Error} If building a database-bearing addon in production without a signing key.
|
|
190
|
+
*/
|
|
145
191
|
function requireAddonSigningKey(signingPrivateKey, developmentBuild) {
|
|
146
192
|
if (signingPrivateKey) return signingPrivateKey;
|
|
147
193
|
if (developmentBuild) return void 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jchat/addon-sdk",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"addon",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
|
-
"src",
|
|
23
22
|
"README.md",
|
|
24
23
|
"CHANGELOG.md"
|
|
25
24
|
],
|
|
@@ -45,7 +44,7 @@
|
|
|
45
44
|
},
|
|
46
45
|
"scripts": {
|
|
47
46
|
"build": "tsdown",
|
|
48
|
-
"lint": "oxlint .
|
|
47
|
+
"lint": "oxlint .",
|
|
49
48
|
"format": "oxfmt . --write",
|
|
50
49
|
"typecheck": "tsc --noEmit",
|
|
51
50
|
"prepack": "bun run build"
|
package/src/contracts.d.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
export type AddonPermission = string;
|
|
2
|
-
/**
|
|
3
|
-
* Defines a single configuration field for an addon's settings panel. These are shown to site
|
|
4
|
-
* admins in the Jchat dashboard.
|
|
5
|
-
*/
|
|
6
|
-
export interface ConfigSchemaField {
|
|
7
|
-
/** Unique key used to store and retrieve the value in `getConfig` */
|
|
8
|
-
key: string;
|
|
9
|
-
/** Data type for validation and UI rendering */
|
|
10
|
-
type: "string" | "number" | "boolean";
|
|
11
|
-
/** Human-readable label shown in the settings UI */
|
|
12
|
-
label: string;
|
|
13
|
-
/** If true, the admin must provide a value before saving */
|
|
14
|
-
required?: boolean;
|
|
15
|
-
/** Whether the value is sensitive (encrypted at rest and masked in the UI) */
|
|
16
|
-
secret?: boolean;
|
|
17
|
-
/** Default value pre-filled in the settings form for new installations */
|
|
18
|
-
default?: string | number | boolean;
|
|
19
|
-
}
|
|
20
|
-
/** Definition of a custom permission introduced by an addon. */
|
|
21
|
-
export interface AddonPermissionDefinition {
|
|
22
|
-
/** Permission identifier (recommended: `addon.<id>.<action>`) */
|
|
23
|
-
name: string;
|
|
24
|
-
/** Short, human-readable name for the permission */
|
|
25
|
-
label: string;
|
|
26
|
-
/** Detailed description of what the permission grants (shown in Rank editor) */
|
|
27
|
-
description: string;
|
|
28
|
-
}
|
|
29
|
-
/** Valid locations where an addon can be rendered within the Jchat layout. */
|
|
30
|
-
export declare const MOUNT_POINTS: {
|
|
31
|
-
/** Top of the main chat column */
|
|
32
|
-
readonly CHAT_HEADER: "chat-header";
|
|
33
|
-
/** Below the main chat header, above messages */
|
|
34
|
-
readonly CHAT_TOOLBAR: "chat-toolbar";
|
|
35
|
-
/** The persistent right-side sidebar */
|
|
36
|
-
readonly CHAT_SIDEBAR: "chat-sidebar";
|
|
37
|
-
/** Top of the left sidebar (room list) */
|
|
38
|
-
readonly SIDEBAR_TOP: "sidebar-top";
|
|
39
|
-
/** Bottom of the left sidebar */
|
|
40
|
-
readonly SIDEBAR_BOTTOM: "sidebar-bottom";
|
|
41
|
-
/** Action list shown when clicking a user's avatar */
|
|
42
|
-
readonly USER_POPOVER_ACTIONS: "user-popover-actions";
|
|
43
|
-
/** Menu shown when right-clicking a message */
|
|
44
|
-
readonly MESSAGE_CONTEXT_MENU: "message-context-menu";
|
|
45
|
-
/** Floating overlay rendered on top of the message stream */
|
|
46
|
-
readonly MESSAGE_FEED_OVERLAY: "message-feed-overlay";
|
|
47
|
-
/** Main admin dashboard area */
|
|
48
|
-
readonly ADMIN_PANEL: "admin-panel";
|
|
49
|
-
/** User or site settings dashboard area */
|
|
50
|
-
readonly SETTINGS_PANEL: "settings-panel";
|
|
51
|
-
/** The 'plus' (+) menu in the message composer */
|
|
52
|
-
readonly CHAT_PLUS_MENU: "chat-plus-menu";
|
|
53
|
-
/** Custom tab in the emoji/gif selection panel */
|
|
54
|
-
readonly EMOJI_PICKER_GIF_TAB: "emoji-picker-gif-tab";
|
|
55
|
-
/** Full-screen or modal dialog specifically for TubeScan */
|
|
56
|
-
readonly TUBESCAN_DIALOG: "tubescan-dialog";
|
|
57
|
-
};
|
|
58
|
-
/** Union type of all valid addon mount points. */
|
|
59
|
-
export type MountPoint = (typeof MOUNT_POINTS)[keyof typeof MOUNT_POINTS];
|
|
60
|
-
/**
|
|
61
|
-
* The `manifest.json` schema required for every Jchat addon. This file describes how the addon is
|
|
62
|
-
* identified and mounted.
|
|
63
|
-
*/
|
|
64
|
-
export interface AddonManifest {
|
|
65
|
-
/** Unique kebab-case addon identifier (must match the folder name) */
|
|
66
|
-
id: string;
|
|
67
|
-
/** Human-readable addon name */
|
|
68
|
-
name: string;
|
|
69
|
-
/** Semver version string (e.g. `1.2.0`) */
|
|
70
|
-
version: string;
|
|
71
|
-
/** Brief summary of what the addon does */
|
|
72
|
-
description?: string;
|
|
73
|
-
/** Author name, organization, or GitHub handle */
|
|
74
|
-
author?: string;
|
|
75
|
-
/** Support or documentation URL */
|
|
76
|
-
homepage?: string;
|
|
77
|
-
/** SPDX license identifier (e.g. `MIT`) */
|
|
78
|
-
license?: string;
|
|
79
|
-
/** Host and SDK versions this directly executed plugin was built and tested against */
|
|
80
|
-
compatibility: {
|
|
81
|
-
/** Supported JChat versions as a semantic-version range, for example `^1.0.0` */
|
|
82
|
-
jchat: string;
|
|
83
|
-
/** Supported `@jchat/addon-sdk` versions as a semantic-version range */
|
|
84
|
-
addonSdk: string;
|
|
85
|
-
};
|
|
86
|
-
/** Identity of the trusted publisher that signed this package. Required when migrations exist */
|
|
87
|
-
publisher?: {
|
|
88
|
-
id: string;
|
|
89
|
-
};
|
|
90
|
-
/** Signature over the canonical archive digest. Required when migrations exist */
|
|
91
|
-
signature?: {
|
|
92
|
-
algorithm: "rsa-sha256";
|
|
93
|
-
digest: string;
|
|
94
|
-
value: string;
|
|
95
|
-
};
|
|
96
|
-
/** Configuration for the React-based frontend bundle */
|
|
97
|
-
frontend?: {
|
|
98
|
-
/** Relative path to the pre-compiled ESM bundle (created by Vite) */
|
|
99
|
-
bundle: string;
|
|
100
|
-
/** List of UI locations where this addon should be active */
|
|
101
|
-
mountPoints: MountPoint[];
|
|
102
|
-
/** Optional path to a specific UI for the settings panel */
|
|
103
|
-
settings?: string;
|
|
104
|
-
/** Additional shared packages this addon imports from the host */
|
|
105
|
-
externals?: string[];
|
|
106
|
-
};
|
|
107
|
-
/** Configuration for the server-side API bundle */
|
|
108
|
-
server?: {
|
|
109
|
-
/** Relative path to the pre-compiled server bundle */
|
|
110
|
-
bundle?: string;
|
|
111
|
-
/** List of SQL files to run (in order) to set up the addon's database tables */
|
|
112
|
-
migrations?: string[];
|
|
113
|
-
/** List of declarative JSON master-data files to apply once, in order */
|
|
114
|
-
seeds?: string[];
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* The minimum permission required to see or use this addon. If provided, the host gates
|
|
118
|
-
* visibility in the UI and access to all server routes.
|
|
119
|
-
*/
|
|
120
|
-
accessPermission?: AddonPermission;
|
|
121
|
-
/** Full list of all permissions used by this addon for informational purposes */
|
|
122
|
-
permissions?: AddonPermission[];
|
|
123
|
-
/** Custom permissions that should be registered with the host application on installation */
|
|
124
|
-
permissionDefinitions?: AddonPermissionDefinition[];
|
|
125
|
-
/** Settings form structure shown to site admins. Values are stored encrypted per site */
|
|
126
|
-
configSchema?: ConfigSchemaField[];
|
|
127
|
-
}
|