@rimori/client 2.5.47 → 2.5.48
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/README.md +2 -2
- package/dist/cli/scripts/init/main.js +1 -1
- package/dist/cli/scripts/init/package-setup.js +0 -1
- package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
- package/dist/cli/scripts/release/release-db-update.js +2 -3
- package/dist/cli/scripts/release/release-prompts-upload.d.ts +1 -2
- package/dist/cli/scripts/release/release-prompts-upload.js +2 -5
- package/dist/cli/scripts/release/release.js +4 -17
- package/dist/fromRimori/PluginTypes.d.ts +10 -3
- package/dist/plugin/CommunicationHandler.d.ts +2 -2
- package/dist/plugin/module/PluginModule.js +3 -1
- package/dist/plugin/module/SharedContentController.d.ts +8 -0
- package/dist/plugin/module/SharedContentController.js +18 -0
- package/package.json +1 -1
- package/dist/cli/scripts/release/detect-translation-languages.d.ts +0 -5
- package/dist/cli/scripts/release/detect-translation-languages.js +0 -32
- package/dist/cli/scripts/release/release-config-upload.d.ts +0 -7
- package/dist/cli/scripts/release/release-config-upload.js +0 -110
- package/dist/cli/scripts/release/release-file-upload.d.ts +0 -6
- package/dist/cli/scripts/release/release-file-upload.js +0 -120
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ npx @rimori/client rimori-init --upgrade # refresh config without changing the
|
|
|
92
92
|
### `rimori-release`
|
|
93
93
|
|
|
94
94
|
- Builds (optionally) and uploads the plugin bundle to Rimori.
|
|
95
|
-
- Updates release metadata, database migrations, and activates the chosen channel (`alpha`, `
|
|
95
|
+
- Updates release metadata, database migrations, and activates the chosen channel (`alpha`, `stable`).
|
|
96
96
|
|
|
97
97
|
Usage:
|
|
98
98
|
|
|
@@ -101,7 +101,7 @@ pnpm build
|
|
|
101
101
|
npx @rimori/client rimori-release alpha
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
During initialization, convenience scripts (`release:alpha`, `release:
|
|
104
|
+
During initialization, convenience scripts (`release:alpha`, `release:stable`) are added to your project automatically.
|
|
105
105
|
|
|
106
106
|
## Runtime API
|
|
107
107
|
|
|
@@ -136,7 +136,7 @@ async function main() {
|
|
|
136
136
|
console.log('');
|
|
137
137
|
console.log(`The plugin should now be accessible at: http://localhost:${3000}`);
|
|
138
138
|
console.log('');
|
|
139
|
-
console.log('If you want to release the plugin, simply run: "pnpm release:<alpha|
|
|
139
|
+
console.log('If you want to release the plugin, simply run: "pnpm release:<alpha|stable>" (details are available in ./rimori/readme.md)');
|
|
140
140
|
}
|
|
141
141
|
catch (error) {
|
|
142
142
|
console.error(`❌ Error: ${error instanceof Error ? error.message : error}`);
|
|
@@ -68,7 +68,6 @@ export function updatePackageJson({ pluginId, port, isUpgrade = false }) {
|
|
|
68
68
|
build: 'pnpm check && vite build',
|
|
69
69
|
check: 'tsc --project tsconfig.app.json --noEmit --pretty',
|
|
70
70
|
'release:alpha': 'pnpm build && pnpm rimori-release alpha',
|
|
71
|
-
'release:beta': 'pnpm build && pnpm rimori-release beta',
|
|
72
71
|
'release:stable': 'pnpm build && pnpm rimori-release stable',
|
|
73
72
|
'dev:worker': 'VITE_MINIFY=false vite build --watch --config worker/vite.config.ts',
|
|
74
73
|
'build:worker': 'vite build --config worker/vite.config.ts',
|
|
@@ -3,4 +3,4 @@ import { Config } from './release';
|
|
|
3
3
|
* Read and send the database configuration to the release endpoint
|
|
4
4
|
* @param config - Configuration object
|
|
5
5
|
*/
|
|
6
|
-
export default function dbUpdate(config: Config
|
|
6
|
+
export default function dbUpdate(config: Config): Promise<void>;
|
|
@@ -5,7 +5,7 @@ import ts from 'typescript';
|
|
|
5
5
|
* Read and send the database configuration to the release endpoint
|
|
6
6
|
* @param config - Configuration object
|
|
7
7
|
*/
|
|
8
|
-
export default async function dbUpdate(config
|
|
8
|
+
export default async function dbUpdate(config) {
|
|
9
9
|
const dbConfigPath = path.resolve('./rimori/db.config.ts');
|
|
10
10
|
// Check if db config file exists
|
|
11
11
|
try {
|
|
@@ -48,11 +48,10 @@ export default async function dbUpdate(config, release_id) {
|
|
|
48
48
|
console.log(`🗄️ Sending database configuration...`);
|
|
49
49
|
const requestBody = {
|
|
50
50
|
db_config: dbConfigObject,
|
|
51
|
-
version: config.version,
|
|
52
51
|
release_channel: config.release_channel,
|
|
53
52
|
plugin_id: config.plugin_id,
|
|
54
53
|
};
|
|
55
|
-
const response = await fetch(`${config.domain}/release
|
|
54
|
+
const response = await fetch(`${config.domain}/release/db`, {
|
|
56
55
|
method: 'POST',
|
|
57
56
|
headers: {
|
|
58
57
|
'Content-Type': 'application/json',
|
|
@@ -3,6 +3,5 @@ import { Config } from './release';
|
|
|
3
3
|
* Read and send the prompts configuration to the release endpoint.
|
|
4
4
|
* Mirrors the pattern of release-db-update.ts.
|
|
5
5
|
* @param config - Configuration object
|
|
6
|
-
* @param release_id - The release ID
|
|
7
6
|
*/
|
|
8
|
-
export default function promptsUpload(config: Config
|
|
7
|
+
export default function promptsUpload(config: Config): Promise<void>;
|
|
@@ -5,9 +5,8 @@ import ts from 'typescript';
|
|
|
5
5
|
* Read and send the prompts configuration to the release endpoint.
|
|
6
6
|
* Mirrors the pattern of release-db-update.ts.
|
|
7
7
|
* @param config - Configuration object
|
|
8
|
-
* @param release_id - The release ID
|
|
9
8
|
*/
|
|
10
|
-
export default async function promptsUpload(config
|
|
9
|
+
export default async function promptsUpload(config) {
|
|
11
10
|
const promptsConfigPath = path.resolve('./rimori/prompts.config.ts');
|
|
12
11
|
// Check if prompts config file exists — optional, skip if not present
|
|
13
12
|
try {
|
|
@@ -48,11 +47,9 @@ export default async function promptsUpload(config, release_id) {
|
|
|
48
47
|
console.log(`📝 Sending ${prompts.length} prompt definitions...`);
|
|
49
48
|
const requestBody = {
|
|
50
49
|
prompts,
|
|
51
|
-
version: config.version,
|
|
52
|
-
release_channel: config.release_channel,
|
|
53
50
|
plugin_id: config.plugin_id,
|
|
54
51
|
};
|
|
55
|
-
const response = await fetch(`${config.domain}/release
|
|
52
|
+
const response = await fetch(`${config.domain}/release/prompts`, {
|
|
56
53
|
method: 'POST',
|
|
57
54
|
headers: {
|
|
58
55
|
'Content-Type': 'application/json',
|
|
@@ -15,8 +15,6 @@ import fs from 'fs';
|
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import dbUpdate from './release-db-update.js';
|
|
17
17
|
import promptsUpload from './release-prompts-upload.js';
|
|
18
|
-
import { uploadDirectory } from './release-file-upload.js';
|
|
19
|
-
import { releasePlugin, sendConfiguration } from './release-config-upload.js';
|
|
20
18
|
// Read version from package.json
|
|
21
19
|
const packageJson = JSON.parse(fs.readFileSync(path.resolve('./package.json'), 'utf8'));
|
|
22
20
|
const { version, r_id: pluginId } = packageJson;
|
|
@@ -61,22 +59,11 @@ async function releaseProcess() {
|
|
|
61
59
|
console.log(`🚀 Releasing ${config.plugin_id} to ${config.release_channel}...`);
|
|
62
60
|
}
|
|
63
61
|
console.log(`📡 Deploying to: ${config.domain}`);
|
|
64
|
-
// First send the configuration
|
|
65
|
-
const release_id = await sendConfiguration(config);
|
|
66
62
|
// Upload prompts (if prompts.config.ts exists)
|
|
67
|
-
await promptsUpload(config
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log('✅ Dev-sync complete');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
// Then upload the files
|
|
75
|
-
await uploadDirectory(config, release_id);
|
|
76
|
-
// Then release the plugin
|
|
77
|
-
await releasePlugin(config, release_id);
|
|
78
|
-
// Inform user about translation processing
|
|
79
|
-
console.log('🌐 Hint: The plugin is released but it might take some time until all translations are being processed.');
|
|
63
|
+
await promptsUpload(config);
|
|
64
|
+
// Migrate tables (if db.config.ts exists)
|
|
65
|
+
await dbUpdate(config);
|
|
66
|
+
console.log(config.dev_sync ? '✅ Dev-sync complete' : '✅ Release complete');
|
|
80
67
|
}
|
|
81
68
|
catch (error) {
|
|
82
69
|
console.log('❌ Error:', error.message);
|
|
@@ -2,8 +2,17 @@ export type Plugin<T extends object = object> = Omit<RimoriPluginConfig<T>, 'con
|
|
|
2
2
|
version: string;
|
|
3
3
|
endpoint: string;
|
|
4
4
|
assetEndpoint: string;
|
|
5
|
+
/**
|
|
6
|
+
* Same-origin base URL for this plugin's UI translations, e.g.
|
|
7
|
+
* `https://app.rimori.se/plugins/<pluginId>`. Set by rimori-main for first-party
|
|
8
|
+
* bundled plugins whose locales it serves itself (module-federation removal); the
|
|
9
|
+
* Translator fetches `<localeUrl>/locales/<lang>.json` from here instead of the
|
|
10
|
+
* plugin's `endpoint`. Undefined for third-party / externally-hosted plugins, which
|
|
11
|
+
* still resolve locales from their `endpoint`.
|
|
12
|
+
*/
|
|
13
|
+
localeUrl?: string;
|
|
5
14
|
context_menu_actions: MenuEntry[];
|
|
6
|
-
release_channel: 'alpha' | '
|
|
15
|
+
release_channel: 'alpha' | 'stable';
|
|
7
16
|
};
|
|
8
17
|
export type ActivePlugin = Plugin<{
|
|
9
18
|
active?: boolean;
|
|
@@ -88,8 +97,6 @@ export interface RimoriPluginConfig<T extends object = object> {
|
|
|
88
97
|
sidebar: (SidebarPage & T)[];
|
|
89
98
|
/** Optional path to the plugin's settings/configuration page */
|
|
90
99
|
settings?: string;
|
|
91
|
-
/** When true, rimori-main loads this plugin via Module Federation instead of an iframe. */
|
|
92
|
-
federated?: boolean;
|
|
93
100
|
/** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
|
|
94
101
|
topics?: string[];
|
|
95
102
|
};
|
|
@@ -32,12 +32,12 @@ export interface RimoriInfo {
|
|
|
32
32
|
/**
|
|
33
33
|
* The release channel of the plugin installation.
|
|
34
34
|
*/
|
|
35
|
-
releaseChannel: 'alpha' | '
|
|
35
|
+
releaseChannel: 'alpha' | 'stable';
|
|
36
36
|
/**
|
|
37
37
|
* The database schema to use for plugin tables.
|
|
38
38
|
* Determined by rimori-main based on release channel:
|
|
39
39
|
* - 'plugins_alpha' for alpha release channel
|
|
40
|
-
* - 'plugins' for
|
|
40
|
+
* - 'plugins' for stable release channel
|
|
41
41
|
*/
|
|
42
42
|
dbSchema: 'plugins' | 'plugins_alpha';
|
|
43
43
|
/**
|
|
@@ -38,7 +38,9 @@ export class PluginModule {
|
|
|
38
38
|
this.communicationHandler = communicationHandler;
|
|
39
39
|
this.eventBus = eventBus;
|
|
40
40
|
const currentPlugin = info.installedPlugins.find((plugin) => plugin.id === info.pluginId);
|
|
41
|
-
|
|
41
|
+
// Prefer localeUrl (same-origin, set by rimori-main for bundled first-party plugins) and
|
|
42
|
+
// fall back to endpoint (storage origin) for third-party / externally-hosted plugins.
|
|
43
|
+
this.translator = new Translator(info.interfaceLanguage, currentPlugin?.localeUrl || currentPlugin?.endpoint || '', ai);
|
|
42
44
|
this.ttsEnabled = info.ttsEnabled ?? true;
|
|
43
45
|
this.communicationHandler.onUpdate((updatedInfo) => {
|
|
44
46
|
// Settings are keyed by guild id — bust the cache if the guild changed so
|
|
@@ -123,6 +123,14 @@ export declare class SharedContentController {
|
|
|
123
123
|
* @returns The shared content item
|
|
124
124
|
*/
|
|
125
125
|
get<T = any>(tableName: string, contentId: string): Promise<SharedContent<T>>;
|
|
126
|
+
/**
|
|
127
|
+
* Get multiple shared content items by ID in a single round-trip.
|
|
128
|
+
* Use instead of `Promise.all(ids.map(id => get(...)))` — that fans out one request per id.
|
|
129
|
+
* @param tableName - Name of the shared content table
|
|
130
|
+
* @param contentIds - IDs of the content to fetch
|
|
131
|
+
* @returns The matching shared content items (fewer than requested if some ids don't exist)
|
|
132
|
+
*/
|
|
133
|
+
getMany<T = any>(tableName: string, contentIds: string[]): Promise<SharedContent<T>[]>;
|
|
126
134
|
/**
|
|
127
135
|
* Fetch all shared content items.
|
|
128
136
|
* @param tableName - Name of the shared content table
|
|
@@ -209,6 +209,24 @@ export class SharedContentController {
|
|
|
209
209
|
}
|
|
210
210
|
return data;
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Get multiple shared content items by ID in a single round-trip.
|
|
214
|
+
* Use instead of `Promise.all(ids.map(id => get(...)))` — that fans out one request per id.
|
|
215
|
+
* @param tableName - Name of the shared content table
|
|
216
|
+
* @param contentIds - IDs of the content to fetch
|
|
217
|
+
* @returns The matching shared content items (fewer than requested if some ids don't exist)
|
|
218
|
+
*/
|
|
219
|
+
async getMany(tableName, contentIds) {
|
|
220
|
+
if (contentIds.length === 0)
|
|
221
|
+
return [];
|
|
222
|
+
const fullTableName = this.getTableName(tableName);
|
|
223
|
+
const { data, error } = await this.supabase.from(fullTableName).select('*').in('id', contentIds);
|
|
224
|
+
if (error) {
|
|
225
|
+
console.error('Error fetching shared content:', error);
|
|
226
|
+
throw new Error('Error fetching shared content');
|
|
227
|
+
}
|
|
228
|
+
return (data || []);
|
|
229
|
+
}
|
|
212
230
|
/**
|
|
213
231
|
* Fetch all shared content items.
|
|
214
232
|
* @param tableName - Name of the shared content table
|
package/package.json
CHANGED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
/**
|
|
3
|
-
* Detect available translation languages from public/locales directory
|
|
4
|
-
* @returns Promise<string[]> Array of language codes found in the locales directory
|
|
5
|
-
*/
|
|
6
|
-
export async function detectTranslationLanguages() {
|
|
7
|
-
const localesPath = './public/locales';
|
|
8
|
-
try {
|
|
9
|
-
await fs.promises.access(localesPath);
|
|
10
|
-
}
|
|
11
|
-
catch (e) {
|
|
12
|
-
console.log('⚠️ No locales directory found, no translations available');
|
|
13
|
-
return [];
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
const files = await fs.promises.readdir(localesPath);
|
|
17
|
-
// Filter out local- files and only include .json files
|
|
18
|
-
const translationFiles = files.filter((file) => file.endsWith('.json') && !file.startsWith('local-'));
|
|
19
|
-
if (translationFiles.length === 0) {
|
|
20
|
-
console.log('⚠️ No translation files found (excluding local- files)');
|
|
21
|
-
return [];
|
|
22
|
-
}
|
|
23
|
-
// Extract language codes from filenames (e.g., en.json -> en)
|
|
24
|
-
const languages = translationFiles.map((file) => file.replace('.json', ''));
|
|
25
|
-
console.log(`🌐 Found ${languages.length} translation languages: ${languages.join(', ')}`);
|
|
26
|
-
return languages;
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
console.error(`❌ Error reading locales directory:`, error.message);
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Config } from './release.js';
|
|
2
|
-
/**
|
|
3
|
-
* Read and send the rimori configuration to the release endpoint
|
|
4
|
-
* @param config - Configuration object
|
|
5
|
-
*/
|
|
6
|
-
export declare function sendConfiguration(config: Config): Promise<string>;
|
|
7
|
-
export declare function releasePlugin(config: Config, release_id: string): Promise<void>;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import ts from 'typescript';
|
|
4
|
-
import { detectTranslationLanguages } from './detect-translation-languages.js';
|
|
5
|
-
/**
|
|
6
|
-
* Read and send the rimori configuration to the release endpoint
|
|
7
|
-
* @param config - Configuration object
|
|
8
|
-
*/
|
|
9
|
-
export async function sendConfiguration(config) {
|
|
10
|
-
const configPath = path.resolve('./rimori/rimori.config.ts');
|
|
11
|
-
// Check if config file exists
|
|
12
|
-
try {
|
|
13
|
-
await fs.promises.access(configPath);
|
|
14
|
-
}
|
|
15
|
-
catch (e) {
|
|
16
|
-
throw new Error('Could not find rimori.config.ts in ./rimori/ directory');
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
let configObject;
|
|
20
|
-
// Use TypeScript compiler to transpile and load
|
|
21
|
-
const configContent = await fs.promises.readFile(configPath, 'utf8');
|
|
22
|
-
// Transpile TypeScript to JavaScript
|
|
23
|
-
const result = ts.transpile(configContent, {
|
|
24
|
-
target: ts.ScriptTarget.ES2020,
|
|
25
|
-
module: ts.ModuleKind.ES2020,
|
|
26
|
-
});
|
|
27
|
-
// Create a temporary file to import the transpiled code
|
|
28
|
-
const tempFile = path.join(process.cwd(), 'temp_config.js');
|
|
29
|
-
await fs.promises.writeFile(tempFile, result);
|
|
30
|
-
try {
|
|
31
|
-
// Use dynamic import to load the config
|
|
32
|
-
const config = await import(`file://${tempFile}`);
|
|
33
|
-
configObject = config.default || config;
|
|
34
|
-
// Clean up temp file
|
|
35
|
-
await fs.promises.unlink(tempFile);
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
// Clean up temp file even on error
|
|
39
|
-
try {
|
|
40
|
-
await fs.promises.unlink(tempFile);
|
|
41
|
-
}
|
|
42
|
-
catch (e) { }
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
if (!configObject) {
|
|
46
|
-
throw new Error('Configuration object is empty or undefined');
|
|
47
|
-
}
|
|
48
|
-
// Detect available translation languages
|
|
49
|
-
const availableLanguages = await detectTranslationLanguages();
|
|
50
|
-
console.log(`🚀 Sending configuration...`);
|
|
51
|
-
const requestBody = {
|
|
52
|
-
config: configObject,
|
|
53
|
-
version: config.version,
|
|
54
|
-
plugin_id: config.plugin_id,
|
|
55
|
-
release_channel: config.release_channel,
|
|
56
|
-
rimori_client_version: config.rimori_client_version,
|
|
57
|
-
provided_languages: availableLanguages.join(','),
|
|
58
|
-
};
|
|
59
|
-
if (config.dev_sync) {
|
|
60
|
-
requestBody.update_existing = true;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
const response = await fetch(`${config.domain}/release`, {
|
|
64
|
-
method: 'POST',
|
|
65
|
-
headers: {
|
|
66
|
-
'Content-Type': 'application/json',
|
|
67
|
-
Authorization: `Bearer ${config.token}`,
|
|
68
|
-
},
|
|
69
|
-
body: JSON.stringify(requestBody),
|
|
70
|
-
});
|
|
71
|
-
const responseText = await response.text();
|
|
72
|
-
console.log('Configuration response status:', response.status);
|
|
73
|
-
console.log('Configuration response text:', responseText);
|
|
74
|
-
const responseData = JSON.parse(responseText);
|
|
75
|
-
if (response.ok) {
|
|
76
|
-
console.log('✅ Configuration deployed successfully!');
|
|
77
|
-
return responseData.release_id;
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
console.log('❌ Configuration failed!');
|
|
81
|
-
console.log('Error:', responseData.error || 'Unknown error');
|
|
82
|
-
console.log('Response data:', JSON.stringify(responseData, null, 2));
|
|
83
|
-
throw new Error('Configuration upload failed');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
console.log('error', e);
|
|
88
|
-
throw new Error('Error sending configuration');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
console.error('❌ Error sending configuration:', error.message);
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
export async function releasePlugin(config, release_id) {
|
|
97
|
-
const response = await fetch(`${config.domain}/release/${release_id}/release`, {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
Authorization: `Bearer ${config.token}`,
|
|
102
|
-
},
|
|
103
|
-
body: JSON.stringify({ plugin_id: config.plugin_id }),
|
|
104
|
-
});
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
console.log('Response:', await response.text());
|
|
107
|
-
throw new Error('Failed to release plugin');
|
|
108
|
-
}
|
|
109
|
-
console.log('✅ Plugin released successfully');
|
|
110
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
/**
|
|
4
|
-
* Upload all files from a directory and its subdirectories to the release function
|
|
5
|
-
* @param config - Configuration object
|
|
6
|
-
*/
|
|
7
|
-
export async function uploadDirectory(config, release_id) {
|
|
8
|
-
const relativePath = './dist';
|
|
9
|
-
console.log(`📁 Preparing to upload files from ${relativePath}...`);
|
|
10
|
-
// Check if dist directory exists
|
|
11
|
-
try {
|
|
12
|
-
await fs.promises.access(relativePath);
|
|
13
|
-
}
|
|
14
|
-
catch (e) {
|
|
15
|
-
throw new Error(`Directory ${relativePath} does not exist. Make sure to build your plugin first.`);
|
|
16
|
-
}
|
|
17
|
-
// Get all files recursively
|
|
18
|
-
const files = await getAllFiles(relativePath);
|
|
19
|
-
if (files.length === 0) {
|
|
20
|
-
console.log('⚠️ No files found to upload');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
console.log(`🚀 Uploading ${files.length} files...`);
|
|
24
|
-
// Create FormData
|
|
25
|
-
const formData = new FormData();
|
|
26
|
-
// Add version and release channel data
|
|
27
|
-
formData.append('version', config.version);
|
|
28
|
-
formData.append('release_channel', config.release_channel);
|
|
29
|
-
formData.append('plugin_id', config.plugin_id);
|
|
30
|
-
// Create path mapping with IDs as keys
|
|
31
|
-
const pathMapping = {};
|
|
32
|
-
for (let i = 0; i < files.length; i++) {
|
|
33
|
-
const filePath = files[i];
|
|
34
|
-
try {
|
|
35
|
-
const fileContent = await fs.promises.readFile(filePath);
|
|
36
|
-
const relativePath = path.relative('./dist', filePath);
|
|
37
|
-
const contentType = getContentType(filePath);
|
|
38
|
-
// Generate unique ID for this file
|
|
39
|
-
const fileId = `file_${i}`;
|
|
40
|
-
// Add to path mapping using ID as key
|
|
41
|
-
pathMapping[fileId] = relativePath;
|
|
42
|
-
// Create a Blob with the file content and content type
|
|
43
|
-
const blob = new Blob([new Uint8Array(fileContent)], { type: contentType });
|
|
44
|
-
// Add file to FormData with ID_filename format
|
|
45
|
-
const fileName = `${fileId}_${path.basename(filePath)}`;
|
|
46
|
-
formData.append('files', blob, fileName);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
console.error(`❌ Error reading file ${filePath}:`, error.message);
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Add path mapping to FormData
|
|
54
|
-
formData.append('path_mapping', JSON.stringify(pathMapping));
|
|
55
|
-
// Upload to the release endpoint
|
|
56
|
-
const response = await fetch(`${config.domain}/release/${release_id}/files`, {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
headers: { Authorization: `Bearer ${config.token}` },
|
|
59
|
-
body: formData,
|
|
60
|
-
});
|
|
61
|
-
if (response.ok) {
|
|
62
|
-
console.log('✅ Files uploaded successfully!');
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
const errorText = await response.text();
|
|
66
|
-
console.log('❌ File upload failed!');
|
|
67
|
-
console.log('Response:', errorText);
|
|
68
|
-
throw new Error(`File upload failed with status ${response.status}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Recursively get all files from a directory
|
|
73
|
-
*/
|
|
74
|
-
async function getAllFiles(dirPath) {
|
|
75
|
-
const files = [];
|
|
76
|
-
async function traverse(currentPath) {
|
|
77
|
-
const entries = await fs.promises.readdir(currentPath, { withFileTypes: true });
|
|
78
|
-
for (const entry of entries) {
|
|
79
|
-
const fullPath = path.join(currentPath, entry.name);
|
|
80
|
-
if (entry.isDirectory()) {
|
|
81
|
-
await traverse(fullPath);
|
|
82
|
-
}
|
|
83
|
-
else if (entry.isFile()) {
|
|
84
|
-
files.push(fullPath);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
await traverse(dirPath);
|
|
89
|
-
return files;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Get content type based on file extension
|
|
93
|
-
*/
|
|
94
|
-
function getContentType(filePath) {
|
|
95
|
-
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
96
|
-
const contentTypes = {
|
|
97
|
-
html: 'text/html',
|
|
98
|
-
css: 'text/css',
|
|
99
|
-
js: 'application/javascript',
|
|
100
|
-
json: 'application/json',
|
|
101
|
-
md: 'text/markdown',
|
|
102
|
-
txt: 'text/plain',
|
|
103
|
-
png: 'image/png',
|
|
104
|
-
jpg: 'image/jpeg',
|
|
105
|
-
jpeg: 'image/jpeg',
|
|
106
|
-
gif: 'image/gif',
|
|
107
|
-
svg: 'image/svg+xml',
|
|
108
|
-
pdf: 'application/pdf',
|
|
109
|
-
ico: 'image/x-icon',
|
|
110
|
-
mp3: 'audio/mpeg',
|
|
111
|
-
wav: 'audio/wav',
|
|
112
|
-
ogg: 'audio/ogg',
|
|
113
|
-
m4a: 'audio/mp4',
|
|
114
|
-
webp: 'image/webp',
|
|
115
|
-
};
|
|
116
|
-
const contentType = contentTypes[ext || ''];
|
|
117
|
-
if (!contentType)
|
|
118
|
-
throw new Error(`Unsupported file type: ${ext}`);
|
|
119
|
-
return contentType;
|
|
120
|
-
}
|