test-bdk-cli 1.0.5 → 1.0.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/commands/run.js +66 -1
- package/dist/lib/localstore.js +208 -0
- package/dist/lib/package.js +13 -0
- package/package.json +5 -2
package/dist/commands/run.js
CHANGED
|
@@ -38,6 +38,7 @@ const http = __importStar(require("http"));
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
|
+
const localstore_1 = require("../lib/localstore");
|
|
41
42
|
function registerRun(program) {
|
|
42
43
|
program
|
|
43
44
|
.command('run [source]')
|
|
@@ -56,8 +57,71 @@ function registerRun(program) {
|
|
|
56
57
|
console.error(`manifest.json not found in ${sourceDir}`);
|
|
57
58
|
process.exit(1);
|
|
58
59
|
}
|
|
59
|
-
//
|
|
60
|
+
// Initialize LocalStore for local database operations
|
|
61
|
+
const localStore = new localstore_1.LocalStore(path.join(sourceDir, '.bdk/localstore'));
|
|
62
|
+
// Handler for database operations
|
|
63
|
+
const handleDBRequest = (operation, key, value) => {
|
|
64
|
+
try {
|
|
65
|
+
switch (operation) {
|
|
66
|
+
case 'set':
|
|
67
|
+
return localStore.store(key, value);
|
|
68
|
+
case 'get':
|
|
69
|
+
return localStore.fetch(key);
|
|
70
|
+
case 'update':
|
|
71
|
+
return localStore.update(key, value);
|
|
72
|
+
case 'delete':
|
|
73
|
+
return localStore.delete(key);
|
|
74
|
+
default:
|
|
75
|
+
throw new Error(`Unknown database operation: ${operation}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error(`[DB] ERROR (${operation} ${key}): ${error.message}`);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
// Create HTTP server
|
|
60
84
|
const server = http.createServer((req, res) => {
|
|
85
|
+
var _a;
|
|
86
|
+
// Add CORS headers
|
|
87
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
88
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
89
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
90
|
+
// Handle OPTIONS requests
|
|
91
|
+
if (req.method === 'OPTIONS') {
|
|
92
|
+
res.writeHead(200);
|
|
93
|
+
res.end();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Handle database API requests
|
|
97
|
+
if (((_a = req.url) === null || _a === void 0 ? void 0 : _a.startsWith('/api/db')) && req.method === 'POST') {
|
|
98
|
+
let body = '';
|
|
99
|
+
req.on('data', (chunk) => {
|
|
100
|
+
body += chunk.toString();
|
|
101
|
+
});
|
|
102
|
+
req.on('error', (error) => {
|
|
103
|
+
console.error(`[DB] Request error:`, error);
|
|
104
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
105
|
+
res.end(JSON.stringify({ success: false, error: 'Request error' }));
|
|
106
|
+
});
|
|
107
|
+
req.on('end', () => {
|
|
108
|
+
try {
|
|
109
|
+
if (!body)
|
|
110
|
+
throw new Error('Empty request body');
|
|
111
|
+
const payload = JSON.parse(body);
|
|
112
|
+
const result = handleDBRequest(payload.operation, payload.key, payload.value);
|
|
113
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
114
|
+
res.end(JSON.stringify({ success: true, data: result }));
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error(`[DB] Error:`, error);
|
|
118
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
119
|
+
res.end(JSON.stringify({ success: false, error: error.message }));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// Handle static file requests
|
|
61
125
|
let filePath = req.url || '/';
|
|
62
126
|
if (filePath === '/') {
|
|
63
127
|
filePath = '/assets/iframe.html';
|
|
@@ -123,6 +187,7 @@ function registerRun(program) {
|
|
|
123
187
|
console.log(`✅ Dev server started on http://localhost:${port} (env=${env})`);
|
|
124
188
|
console.log(`📁 Serving from: ${sourceDir}`);
|
|
125
189
|
console.log(`📄 Iframe: http://localhost:${port}/assets/iframe.html`);
|
|
190
|
+
console.log(`💾 Database: Local storage at ${localStore.getFilePath()}`);
|
|
126
191
|
console.log(`\nPress Ctrl+C to stop the server\n`);
|
|
127
192
|
// Open browser if --open flag is set
|
|
128
193
|
if (opts.open) {
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BoldDesk LocalStore - Persistent local data storage during development
|
|
4
|
+
*
|
|
5
|
+
* Similar to Freshdesk FDK's DataStore (lib/utils/data.js)
|
|
6
|
+
* Stores key-value data in .bolddesk/localstore JSON file
|
|
7
|
+
*
|
|
8
|
+
* Key Format: '1234' or '1234:{jiraId:9878}' (first part is the key)
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.LocalStore = void 0;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
/**
|
|
48
|
+
* LocalStore class for managing persistent local storage
|
|
49
|
+
* Mirrors Freshdesk's DataStore functionality
|
|
50
|
+
*/
|
|
51
|
+
class LocalStore {
|
|
52
|
+
constructor(filePath = '.bdk/localstore') {
|
|
53
|
+
this.data = {};
|
|
54
|
+
this.filePath = filePath;
|
|
55
|
+
this.ensureStore();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Ensure localstore folder and file exist
|
|
59
|
+
* Creates folder and file if they don't exist
|
|
60
|
+
* Handles case where folder is deleted mid-operation
|
|
61
|
+
*/
|
|
62
|
+
ensureStore() {
|
|
63
|
+
const folder = path.dirname(this.filePath);
|
|
64
|
+
try {
|
|
65
|
+
// Create folder if it doesn't exist
|
|
66
|
+
if (!fs.existsSync(folder)) {
|
|
67
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
// Create file if it doesn't exist
|
|
70
|
+
if (!fs.existsSync(this.filePath)) {
|
|
71
|
+
fs.writeFileSync(this.filePath, JSON.stringify({}));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`[LocalStore] Error in ensureStore: ${error.message}`);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate key format and constraints
|
|
81
|
+
* - Max 60 characters
|
|
82
|
+
* - Cannot be empty
|
|
83
|
+
* - Alphanumeric, dash, underscore, colon allowed
|
|
84
|
+
*/
|
|
85
|
+
validateKey(key) {
|
|
86
|
+
if (!key || typeof key !== 'string') {
|
|
87
|
+
throw new Error('The key cannot be blank');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read localstore file from disk
|
|
92
|
+
* Returns parsed JSON object
|
|
93
|
+
* Returns empty object on error
|
|
94
|
+
*/
|
|
95
|
+
readLocalStore() {
|
|
96
|
+
try {
|
|
97
|
+
if (!fs.existsSync(this.filePath)) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
const content = fs.readFileSync(this.filePath, 'utf-8');
|
|
101
|
+
return JSON.parse(content);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
console.error(`[LocalStore] Error reading file: ${e.message}`);
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Write localstore file to disk
|
|
110
|
+
* Pretty-prints JSON for human readability
|
|
111
|
+
* Recreates folder if it was deleted
|
|
112
|
+
*/
|
|
113
|
+
writeLocalStore(data) {
|
|
114
|
+
try {
|
|
115
|
+
const folder = path.dirname(this.filePath);
|
|
116
|
+
// Explicitly ensure folder exists before writing
|
|
117
|
+
if (!fs.existsSync(folder)) {
|
|
118
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
119
|
+
}
|
|
120
|
+
// Write the file
|
|
121
|
+
fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
console.error(`[LocalStore] Error writing file: ${e.message}`);
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Store operation - SET key/value
|
|
130
|
+
* Stores data as-is without modification
|
|
131
|
+
*
|
|
132
|
+
* @param key Storage key (e.g., 'ticket-001')
|
|
133
|
+
* @param data Value to store (JSON object)
|
|
134
|
+
* @returns { Created: true }
|
|
135
|
+
*/
|
|
136
|
+
store(key, data) {
|
|
137
|
+
this.validateKey(key);
|
|
138
|
+
this.ensureStore();
|
|
139
|
+
const localStoreData = this.readLocalStore();
|
|
140
|
+
localStoreData[key] = data;
|
|
141
|
+
this.writeLocalStore(localStoreData);
|
|
142
|
+
return { Created: true };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Fetch operation - GET key/value
|
|
146
|
+
* Retrieves stored data for a key
|
|
147
|
+
*
|
|
148
|
+
* @param key Storage key to retrieve
|
|
149
|
+
* @returns Stored data or undefined if not found
|
|
150
|
+
*/
|
|
151
|
+
fetch(key) {
|
|
152
|
+
const localStoreData = this.readLocalStore();
|
|
153
|
+
return localStoreData[key];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Update operation - PATCH key with new values
|
|
157
|
+
* Merges attributes with existing data
|
|
158
|
+
*
|
|
159
|
+
* @param key Storage key to update
|
|
160
|
+
* @param attributes Object with fields to merge
|
|
161
|
+
* @returns { Updated: true }
|
|
162
|
+
*/
|
|
163
|
+
update(key, attributes) {
|
|
164
|
+
this.validateKey(key);
|
|
165
|
+
this.ensureStore();
|
|
166
|
+
const localStoreData = this.readLocalStore();
|
|
167
|
+
const existingRecord = localStoreData[key];
|
|
168
|
+
if (!existingRecord) {
|
|
169
|
+
throw new Error(`Record not found for key: ${key}`);
|
|
170
|
+
}
|
|
171
|
+
localStoreData[key] = { ...existingRecord, ...attributes };
|
|
172
|
+
this.writeLocalStore(localStoreData);
|
|
173
|
+
return { Updated: true };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Delete operation - DELETE key
|
|
177
|
+
* Similar to Freshdesk DataStore.delete()
|
|
178
|
+
*
|
|
179
|
+
* @param key Storage key to delete
|
|
180
|
+
* @returns { Deleted: true }
|
|
181
|
+
*/
|
|
182
|
+
delete(key) {
|
|
183
|
+
this.ensureStore();
|
|
184
|
+
const localStoreData = this.readLocalStore();
|
|
185
|
+
delete localStoreData[key];
|
|
186
|
+
this.writeLocalStore(localStoreData);
|
|
187
|
+
return { Deleted: true };
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get the file path to localstore
|
|
191
|
+
*/
|
|
192
|
+
getFilePath() {
|
|
193
|
+
return this.filePath;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get all stored data (for debugging)
|
|
197
|
+
*/
|
|
198
|
+
getAll() {
|
|
199
|
+
return this.readLocalStore();
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Clear all data from localstore
|
|
203
|
+
*/
|
|
204
|
+
clear() {
|
|
205
|
+
this.writeLocalStore({});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.LocalStore = LocalStore;
|
package/dist/lib/package.js
CHANGED
|
@@ -129,6 +129,19 @@ async function validatePkg(appPath) {
|
|
|
129
129
|
errors.push(`settings.fields[${fieldLabel}]: 'type' is required for each field object.`);
|
|
130
130
|
}
|
|
131
131
|
});
|
|
132
|
+
// ── Rule: settings.fields key uniqueness ──
|
|
133
|
+
const seenKeys = new Map();
|
|
134
|
+
settings.fields.forEach((field, index) => {
|
|
135
|
+
if (field && typeof field.key === 'string' && field.key.trim() !== '') {
|
|
136
|
+
const key = field.key.trim();
|
|
137
|
+
if (seenKeys.has(key)) {
|
|
138
|
+
errors.push(`settings.fields: duplicate key "${key}" found at index ${index} (first used at index ${seenKeys.get(key)}). Each field key must be unique.`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
seenKeys.set(key, index);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
132
145
|
}
|
|
133
146
|
// ── Rule: enablePermission / enableReadPermission / enableWritePermission ──
|
|
134
147
|
// If enablePermission is true → at least one of enableReadPermission or enableWritePermission must be true.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "test-bdk-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "test CLI",
|
|
5
5
|
"author": "@BoldDesk",
|
|
6
6
|
"bin": {
|
|
@@ -61,7 +61,10 @@
|
|
|
61
61
|
"/src/"
|
|
62
62
|
],
|
|
63
63
|
"transform": {
|
|
64
|
-
"^.+\\.tsx?$": [
|
|
64
|
+
"^.+\\.tsx?$": [
|
|
65
|
+
"ts-jest",
|
|
66
|
+
{}
|
|
67
|
+
]
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
}
|